<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <stdint.h>

#include <algorithm>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>

#include <process/collect.hpp>
#include <process/defer.hpp>
#include <process/future.hpp>

#include <stout/error.hpp>
#include <stout/foreach.hpp>
#include <stout/hashmap.hpp>
#include <stout/option.hpp>
#include <stout/os.hpp>
#include <stout/try.hpp>

#include "linux/cgroups.hpp"

#include "slave/flags.hpp"

#include "slave/containerizer/mesos/isolator.hpp"

#include "slave/containerizer/mesos/isolators/gpu/nvidia.hpp"
#include "slave/containerizer/mesos/isolators/gpu/nvml.hpp"

using cgroups::devices::Entry;

using mesos::slave::ContainerConfig;
using mesos::slave::ContainerLaunchInfo;
using mesos::slave::ContainerLimitation;
using mesos::slave::ContainerState;
using mesos::slave::Isolator;

using process::Failure;
using process::Future;
using process::PID;

using std::list;
using std::map;
using std::set;
using std::string;
using std::vector;

namespace mesos {
namespace internal {
namespace slave {

// TODO(klueska): Expand this when we support other GPU types.
static constexpr unsigned int NVIDIA_MAJOR_DEVICE = 195;

NvidiaGpuIsolatorProcess::NvidiaGpuIsolatorProcess(
    const Flags& _flags,
    const string& _hierarchy,
    const list<Gpu>& gpus,
    const cgroups::devices::Entry& uvmDeviceEntry,
    const cgroups::devices::Entry& ctlDeviceEntry)
  : flags(_flags),
    hierarchy(_hierarchy),
    available(gpus),
    NVIDIA_CTL_DEVICE_ENTRY(ctlDeviceEntry),
    NVIDIA_UVM_DEVICE_ENTRY(uvmDeviceEntry) {}

Try<Isolator*> NvidiaGpuIsolatorProcess::create(const Flags& flags)
{
  // Make sure the 'cgroups/devices' isolator is present and
  // precedes the GPU isolator.
  vector<string> tokens = strings::tokenize(flags.isolation, ",");

  auto gpuIsolator =
    std::find(tokens.begin(), tokens.end(), "gpu/nvidia");
  auto devicesIsolator =
    std::find(tokens.begin(), tokens.end(), "cgroups/devices");

  CHECK(gpuIsolator != tokens.end());

  if (devicesIsolator == tokens.end()) {
    return Error("The 'cgroups/devices' isolator must be enabled in"
                 " order to use the gpu/devices isolator");
  }

  if (devicesIsolator > gpuIsolator) {
    return Error("'cgroups/devices' must precede 'gpu/nvidia'"
                 " in the --isolation flag");
  }

  // Initialize NVML.
  Try<Nothing> initialize = nvml::initialize();
  if (initialize.isError()) {
    return Error("Failed to initialize nvml: " + initialize.error());
  }

  // Enumerate all available GPU devices.
  list<Gpu> gpus;

  if (flags.nvidia_gpu_devices.isSome()) {
    foreach (unsigned int index, flags.nvidia_gpu_devices.get()) {
      Try<nvmlDevice_t> handle = nvml::deviceGetHandleByIndex(index);
      if (handle.isError()) {
        return Error("Failed to obtain Nvidia device handle for"
                     " index " + stringify(index) + ": " + handle.error());
      }

      Try<unsigned int> minor = nvml::deviceGetMinorNumber(handle.get());
      if (minor.isError()) {
        return Error("Failed to obtain Nvidia device minor number: " +
                     minor.error());
      }

      Gpu gpu;
      gpu.handle = handle.get();
      gpu.major = NVIDIA_MAJOR_DEVICE;
      gpu.minor = minor.get();

      gpus.push_back(gpu);
    }
  }

  // Retrieve the cgroups devices hierarchy.
  Result<string> hierarchy = cgroups::hierarchy("devices");

  if (hierarchy.isError()) {
    return Error(
        "Error retrieving the 'devices' subsystem hierarchy: " +
        hierarchy.error());
  }

  // Create the device entries for
  // `/dev/nvidiactl` and `/dev/nvidia-uvm`.
  Try<dev_t> device = os::stat::rdev("/dev/nvidiactl");
  if (device.isError()) {
    return Error("Failed to obtain device ID for '/dev/nvidiactl': " +
                 device.error());
  }

  cgroups::devices::Entry ctlDeviceEntry;
  ctlDeviceEntry.selector.type = Entry::Selector::Type::CHARACTER;
  ctlDeviceEntry.selector.major = major(device.get());
  ctlDeviceEntry.selector.minor = minor(device.get());
  ctlDeviceEntry.access.read = true;
  ctlDeviceEntry.access.write = true;
  ctlDeviceEntry.access.mknod = true;

  device = os::stat::rdev("/dev/nvidia-uvm");
  if (device.isError()) {
    return Error("Failed to obtain device ID for '/dev/nvidia-uvm': " +
                 device.error());
  }

  cgroups::devices::Entry uvmDeviceEntry;
  uvmDeviceEntry.selector.type = Entry::Selector::Type::CHARACTER;
  uvmDeviceEntry.selector.major = major(device.get());
  uvmDeviceEntry.selector.minor = minor(device.get());
  uvmDeviceEntry.access.read = true;
  uvmDeviceEntry.access.write = true;
  uvmDeviceEntry.access.mknod = true;

  process::Owned<MesosIsolatorProcess> process(
      new NvidiaGpuIsolatorProcess(
          flags,
          hierarchy.get(),
          gpus,
          ctlDeviceEntry,
          uvmDeviceEntry));

  return new MesosIsolator(process);
}

Future<Nothing> NvidiaGpuIsolatorProcess::recover(
    const list<ContainerState>& states,
    const hashset<ContainerID>& orphans)
{
  foreach (const ContainerState& state, states) {
    const ContainerID& containerId = state.container_id();
    const string cgroup = path::join(flags.cgroups_root, containerId.value());

    Try<bool> exists = cgroups::exists(hierarchy, cgroup);
    if (exists.isError()) {
      foreachvalue (Info* info, infos) {
        delete info;
      }
      infos.clear();
      return Failure("Failed to check cgroup for container '" +
                     stringify(containerId) + "'");
    }

    if (!exists.get()) {
      VLOG(1) << "Couldn't find cgroup for container " << containerId;
      // This may occur if the executor has exited and the isolator
      // has destroyed the cgroup but the slave dies before noticing
      // this. This will be detected when the containerizer tries to
      // monitor the executor's pid.
      continue;
    }

    infos[containerId] = new Info(containerId, cgroup);

    // Determine which GPUs are allocated to this container.
    Try<vector<cgroups::devices::Entry>> entries =
      cgroups::devices::list(hierarchy, cgroup);

    if (entries.isError()) {
      return Failure("Failed to obtain devices list for cgroup"
                     " '" + cgroup + "': " + entries.error());
    }

    foreach (const cgroups::devices::Entry& entry, entries.get()) {
      for (auto gpu = available.begin(); gpu != available.end(); ++gpu) {
        if (entry.selector.major == gpu->major &&
            entry.selector.minor == gpu->minor) {
          infos[containerId]->allocated.push_back(*gpu);
          available.erase(gpu);
          break;
        }
      }
    }
  }

  return Nothing();
}

Future<Option<ContainerLaunchInfo>> NvidiaGpuIsolatorProcess::prepare(
    const ContainerID& containerId,
    const mesos::slave::ContainerConfig& containerConfig)
{
  if (infos.contains(containerId)) {
    return Failure("Container has already been prepared");
  }

  infos[containerId] = new Info(
      containerId, path::join(flags.cgroups_root, containerId.value()));

  return update(containerId, containerConfig.executor_info().resources())
    .then([]() -> Future<Option<ContainerLaunchInfo>> {
      return None();
    });
}

Future<Nothing> NvidiaGpuIsolatorProcess::update(
    const ContainerID& containerId,
    const Resources& resources)
{
  if (!infos.contains(containerId)) {
    return Failure("Unknown container");
  }

  Info* info = CHECK_NOTNULL(infos[containerId]);

  Option<double> gpus = resources.gpus();

  // Make sure that the `gpus` resource is not fractional.
  // We rely on scalar resources only having 3 digits of precision.
  if (static_cast<long long>(gpus.getOrElse(0.0) * 1000.0) % 1000 != 0) {
    return Failure("The 'gpus' resource must be an unsigned integer");
  }

  size_t requested = static_cast<size_t>(resources.gpus().getOrElse(0.0));

  // Update the GPU allocation to reflect the new total.
  if (requested > info->allocated.size()) {
    size_t additional = requested - info->allocated.size();

    if (additional > available.size()) {
      return Failure("Not enough GPUs available to reserve"
                     " " + stringify(additional) + " additional GPUs");
    }

    // Grant access to /dev/nvidiactl and /dev/nvida-uvm
    // if this container is about to get its first GPU.
    if (info->allocated.empty()) {
      map<string, cgroups::devices::Entry> entries = {
        { "/dev/nvidiactl", NVIDIA_CTL_DEVICE_ENTRY },
        { "/dev/nvidia-uvm", NVIDIA_UVM_DEVICE_ENTRY },
      };

      foreachkey (const string& device, entries) {
        Try<Nothing> allow = cgroups::devices::allow(
            hierarchy, info->cgroup, entries[device]);

        if (allow.isError()) {
          return Failure("Failed to grant cgroups access to"
                         " '" + device + "': " + allow.error());
        }
      }
    }

    for (size_t i = 0; i < additional; i++) {
      const Gpu& gpu = available.front();

      cgroups::devices::Entry entry;
      entry.selector.type = Entry::Selector::Type::CHARACTER;
      entry.selector.major = gpu.major;
      entry.selector.minor = gpu.minor;
      entry.access.read = true;
      entry.access.write = true;
      entry.access.mknod = true;

      Try<Nothing> allow = cgroups::devices::allow(
          hierarchy, info->cgroup, entry);

      if (allow.isError()) {
        return Failure("Failed to grant cgroups access to GPU device"
                       " '" + stringify(entry) + "': " + allow.error());
      }

      info->allocated.push_back(gpu);
      available.pop_front();
    }
  } else if (requested < info->allocated.size()) {
    size_t fewer = info->allocated.size() - requested;

    for (size_t i = 0; i < fewer; i++) {
      const Gpu& gpu = info->allocated.front();

      cgroups::devices::Entry entry;
      entry.selector.type = Entry::Selector::Type::CHARACTER;
      entry.selector.major = gpu.major;
      entry.selector.minor = gpu.minor;
      entry.access.read = true;
      entry.access.write = true;
      entry.access.mknod = true;

      Try<Nothing> deny = cgroups::devices::deny(
          hierarchy, info->cgroup, entry);

      if (deny.isError()) {
        return Failure("Failed to deny cgroups access to GPU device"
                       " '" + stringify(entry) + "': " + deny.error());
      }

      info->allocated.pop_front();
      available.push_back(gpu);
    }

    // Revoke access from /dev/nvidiactl and /dev/nvida-uvm
    // if this container no longer has access to any GPUs.
    if (info->allocated.empty()) {
      map<string, cgroups::devices::Entry> entries = {
        { "/dev/nvidiactl", NVIDIA_CTL_DEVICE_ENTRY },
        { "/dev/nvidia-uvm", NVIDIA_UVM_DEVICE_ENTRY },
      };

      foreachkey (const string& device, entries) {
        Try<Nothing> deny = cgroups::devices::deny(
            hierarchy, info->cgroup, entries[device]);

        if (deny.isError()) {
          return Failure("Failed to deny cgroups access to"
                         " '" + device + "': " + deny.error());
        }
      }
    }
  }

  return Nothing();
}

Future<ResourceStatistics> NvidiaGpuIsolatorProcess::usage(
    const ContainerID& containerId)
{
  if (!infos.contains(containerId)) {
    return Failure("Unknown container");
  }

  // TODO(rtodd): Obtain usage information from NVML.

  ResourceStatistics result;
  return result;
}

Future<Nothing> NvidiaGpuIsolatorProcess::cleanup(
    const ContainerID& containerId)
{
  // Multiple calls may occur during test clean up.
  if (!infos.contains(containerId)) {
    VLOG(1) << "Ignoring cleanup request for unknown container " << containerId;

    return Nothing();
  }

  Info* info = CHECK_NOTNULL(infos[containerId]);

  // Make any remaining GPUs available.
  available.splice(available.end(), info->allocated);

  delete info;
  infos.erase(containerId);

  return Nothing();
}

} // namespace slave {
} // namespace internal {
} // namespace mesos {
<commit_msg>Always grant access to NVIDIA control devices.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <stdint.h>

#include <algorithm>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>

#include <process/collect.hpp>
#include <process/defer.hpp>
#include <process/future.hpp>

#include <stout/error.hpp>
#include <stout/foreach.hpp>
#include <stout/hashmap.hpp>
#include <stout/option.hpp>
#include <stout/os.hpp>
#include <stout/try.hpp>

#include "linux/cgroups.hpp"

#include "slave/flags.hpp"

#include "slave/containerizer/mesos/isolator.hpp"

#include "slave/containerizer/mesos/isolators/gpu/nvidia.hpp"
#include "slave/containerizer/mesos/isolators/gpu/nvml.hpp"

using cgroups::devices::Entry;

using mesos::slave::ContainerConfig;
using mesos::slave::ContainerLaunchInfo;
using mesos::slave::ContainerLimitation;
using mesos::slave::ContainerState;
using mesos::slave::Isolator;

using process::Failure;
using process::Future;
using process::PID;

using std::list;
using std::map;
using std::set;
using std::string;
using std::vector;

namespace mesos {
namespace internal {
namespace slave {

// TODO(klueska): Expand this when we support other GPU types.
static constexpr unsigned int NVIDIA_MAJOR_DEVICE = 195;

NvidiaGpuIsolatorProcess::NvidiaGpuIsolatorProcess(
    const Flags& _flags,
    const string& _hierarchy,
    const list<Gpu>& gpus,
    const cgroups::devices::Entry& uvmDeviceEntry,
    const cgroups::devices::Entry& ctlDeviceEntry)
  : flags(_flags),
    hierarchy(_hierarchy),
    available(gpus),
    NVIDIA_CTL_DEVICE_ENTRY(ctlDeviceEntry),
    NVIDIA_UVM_DEVICE_ENTRY(uvmDeviceEntry) {}

Try<Isolator*> NvidiaGpuIsolatorProcess::create(const Flags& flags)
{
  // Make sure the 'cgroups/devices' isolator is present and
  // precedes the GPU isolator.
  vector<string> tokens = strings::tokenize(flags.isolation, ",");

  auto gpuIsolator =
    std::find(tokens.begin(), tokens.end(), "gpu/nvidia");
  auto devicesIsolator =
    std::find(tokens.begin(), tokens.end(), "cgroups/devices");

  CHECK(gpuIsolator != tokens.end());

  if (devicesIsolator == tokens.end()) {
    return Error("The 'cgroups/devices' isolator must be enabled in"
                 " order to use the gpu/devices isolator");
  }

  if (devicesIsolator > gpuIsolator) {
    return Error("'cgroups/devices' must precede 'gpu/nvidia'"
                 " in the --isolation flag");
  }

  // Initialize NVML.
  Try<Nothing> initialize = nvml::initialize();
  if (initialize.isError()) {
    return Error("Failed to initialize nvml: " + initialize.error());
  }

  // Enumerate all available GPU devices.
  list<Gpu> gpus;

  if (flags.nvidia_gpu_devices.isSome()) {
    foreach (unsigned int index, flags.nvidia_gpu_devices.get()) {
      Try<nvmlDevice_t> handle = nvml::deviceGetHandleByIndex(index);
      if (handle.isError()) {
        return Error("Failed to obtain Nvidia device handle for"
                     " index " + stringify(index) + ": " + handle.error());
      }

      Try<unsigned int> minor = nvml::deviceGetMinorNumber(handle.get());
      if (minor.isError()) {
        return Error("Failed to obtain Nvidia device minor number: " +
                     minor.error());
      }

      Gpu gpu;
      gpu.handle = handle.get();
      gpu.major = NVIDIA_MAJOR_DEVICE;
      gpu.minor = minor.get();

      gpus.push_back(gpu);
    }
  }

  // Retrieve the cgroups devices hierarchy.
  Result<string> hierarchy = cgroups::hierarchy("devices");

  if (hierarchy.isError()) {
    return Error(
        "Error retrieving the 'devices' subsystem hierarchy: " +
        hierarchy.error());
  }

  // Create the device entries for
  // `/dev/nvidiactl` and `/dev/nvidia-uvm`.
  Try<dev_t> device = os::stat::rdev("/dev/nvidiactl");
  if (device.isError()) {
    return Error("Failed to obtain device ID for '/dev/nvidiactl': " +
                 device.error());
  }

  cgroups::devices::Entry ctlDeviceEntry;
  ctlDeviceEntry.selector.type = Entry::Selector::Type::CHARACTER;
  ctlDeviceEntry.selector.major = major(device.get());
  ctlDeviceEntry.selector.minor = minor(device.get());
  ctlDeviceEntry.access.read = true;
  ctlDeviceEntry.access.write = true;
  ctlDeviceEntry.access.mknod = true;

  device = os::stat::rdev("/dev/nvidia-uvm");
  if (device.isError()) {
    return Error("Failed to obtain device ID for '/dev/nvidia-uvm': " +
                 device.error());
  }

  cgroups::devices::Entry uvmDeviceEntry;
  uvmDeviceEntry.selector.type = Entry::Selector::Type::CHARACTER;
  uvmDeviceEntry.selector.major = major(device.get());
  uvmDeviceEntry.selector.minor = minor(device.get());
  uvmDeviceEntry.access.read = true;
  uvmDeviceEntry.access.write = true;
  uvmDeviceEntry.access.mknod = true;

  process::Owned<MesosIsolatorProcess> process(
      new NvidiaGpuIsolatorProcess(
          flags,
          hierarchy.get(),
          gpus,
          ctlDeviceEntry,
          uvmDeviceEntry));

  return new MesosIsolator(process);
}

Future<Nothing> NvidiaGpuIsolatorProcess::recover(
    const list<ContainerState>& states,
    const hashset<ContainerID>& orphans)
{
  foreach (const ContainerState& state, states) {
    const ContainerID& containerId = state.container_id();
    const string cgroup = path::join(flags.cgroups_root, containerId.value());

    Try<bool> exists = cgroups::exists(hierarchy, cgroup);
    if (exists.isError()) {
      foreachvalue (Info* info, infos) {
        delete info;
      }
      infos.clear();
      return Failure("Failed to check cgroup for container '" +
                     stringify(containerId) + "'");
    }

    if (!exists.get()) {
      VLOG(1) << "Couldn't find cgroup for container " << containerId;
      // This may occur if the executor has exited and the isolator
      // has destroyed the cgroup but the slave dies before noticing
      // this. This will be detected when the containerizer tries to
      // monitor the executor's pid.
      continue;
    }

    infos[containerId] = new Info(containerId, cgroup);

    // Determine which GPUs are allocated to this container.
    Try<vector<cgroups::devices::Entry>> entries =
      cgroups::devices::list(hierarchy, cgroup);

    if (entries.isError()) {
      return Failure("Failed to obtain devices list for cgroup"
                     " '" + cgroup + "': " + entries.error());
    }

    foreach (const cgroups::devices::Entry& entry, entries.get()) {
      for (auto gpu = available.begin(); gpu != available.end(); ++gpu) {
        if (entry.selector.major == gpu->major &&
            entry.selector.minor == gpu->minor) {
          infos[containerId]->allocated.push_back(*gpu);
          available.erase(gpu);
          break;
        }
      }
    }
  }

  return Nothing();
}

Future<Option<ContainerLaunchInfo>> NvidiaGpuIsolatorProcess::prepare(
    const ContainerID& containerId,
    const mesos::slave::ContainerConfig& containerConfig)
{
  if (infos.contains(containerId)) {
    return Failure("Container has already been prepared");
  }

  infos[containerId] = new Info(
      containerId, path::join(flags.cgroups_root, containerId.value()));

  // Grant access to /dev/nvidiactl and /dev/nvida-uvm
  map<string, const cgroups::devices::Entry> entries = {
    { "/dev/nvidiactl", NVIDIA_CTL_DEVICE_ENTRY },
    { "/dev/nvidia-uvm", NVIDIA_UVM_DEVICE_ENTRY },
  };

  foreachkey (const string& device, entries) {
    Try<Nothing> allow = cgroups::devices::allow(
        hierarchy, infos[containerId]->cgroup, entries[device]);

    if (allow.isError()) {
      return Failure("Failed to grant cgroups access to"
                     " '" + device + "': " + allow.error());
    }
  }

  return update(containerId, containerConfig.executor_info().resources())
    .then([]() -> Future<Option<ContainerLaunchInfo>> {
      return None();
    });
}

Future<Nothing> NvidiaGpuIsolatorProcess::update(
    const ContainerID& containerId,
    const Resources& resources)
{
  if (!infos.contains(containerId)) {
    return Failure("Unknown container");
  }

  Info* info = CHECK_NOTNULL(infos[containerId]);

  Option<double> gpus = resources.gpus();

  // Make sure that the `gpus` resource is not fractional.
  // We rely on scalar resources only having 3 digits of precision.
  if (static_cast<long long>(gpus.getOrElse(0.0) * 1000.0) % 1000 != 0) {
    return Failure("The 'gpus' resource must be an unsigned integer");
  }

  size_t requested = static_cast<size_t>(resources.gpus().getOrElse(0.0));

  // Update the GPU allocation to reflect the new total.
  if (requested > info->allocated.size()) {
    size_t additional = requested - info->allocated.size();

    if (additional > available.size()) {
      return Failure("Not enough GPUs available to reserve"
                     " " + stringify(additional) + " additional GPUs");
    }

    for (size_t i = 0; i < additional; i++) {
      const Gpu& gpu = available.front();

      cgroups::devices::Entry entry;
      entry.selector.type = Entry::Selector::Type::CHARACTER;
      entry.selector.major = gpu.major;
      entry.selector.minor = gpu.minor;
      entry.access.read = true;
      entry.access.write = true;
      entry.access.mknod = true;

      Try<Nothing> allow = cgroups::devices::allow(
          hierarchy, info->cgroup, entry);

      if (allow.isError()) {
        return Failure("Failed to grant cgroups access to GPU device"
                       " '" + stringify(entry) + "': " + allow.error());
      }

      info->allocated.push_back(gpu);
      available.pop_front();
    }
  } else if (requested < info->allocated.size()) {
    size_t fewer = info->allocated.size() - requested;

    for (size_t i = 0; i < fewer; i++) {
      const Gpu& gpu = info->allocated.front();

      cgroups::devices::Entry entry;
      entry.selector.type = Entry::Selector::Type::CHARACTER;
      entry.selector.major = gpu.major;
      entry.selector.minor = gpu.minor;
      entry.access.read = true;
      entry.access.write = true;
      entry.access.mknod = true;

      Try<Nothing> deny = cgroups::devices::deny(
          hierarchy, info->cgroup, entry);

      if (deny.isError()) {
        return Failure("Failed to deny cgroups access to GPU device"
                       " '" + stringify(entry) + "': " + deny.error());
      }

      info->allocated.pop_front();
      available.push_back(gpu);
    }
  }

  return Nothing();
}

Future<ResourceStatistics> NvidiaGpuIsolatorProcess::usage(
    const ContainerID& containerId)
{
  if (!infos.contains(containerId)) {
    return Failure("Unknown container");
  }

  // TODO(rtodd): Obtain usage information from NVML.

  ResourceStatistics result;
  return result;
}

Future<Nothing> NvidiaGpuIsolatorProcess::cleanup(
    const ContainerID& containerId)
{
  // Multiple calls may occur during test clean up.
  if (!infos.contains(containerId)) {
    VLOG(1) << "Ignoring cleanup request for unknown container " << containerId;

    return Nothing();
  }

  Info* info = CHECK_NOTNULL(infos[containerId]);

  // Make any remaining GPUs available.
  available.splice(available.end(), info->allocated);

  delete info;
  infos.erase(containerId);

  return Nothing();
}

} // namespace slave {
} // namespace internal {
} // namespace mesos {
<|endoftext|>


<commit_before>/*****************************************************************************
 * adaptative.cpp: Adaptative streaming module
 *****************************************************************************
 * Copyright © 2015 - VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdint.h>

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>

#include "playlist/BasePeriod.h"
#include "xml/DOMParser.h"

#include "../dash/DASHManager.h"
#include "../dash/DASHStream.hpp"
#include "../dash/mpd/IsoffMainParser.h"

#include "../hls/HLSManager.hpp"
#include "../hls/HLSStreams.hpp"
#include "../hls/playlist/Parser.hpp"
#include "../hls/playlist/M3U8.hpp"
#include "../smooth/SmoothManager.hpp"
#include "../smooth/SmoothStream.hpp"
#include "../smooth/playlist/Parser.hpp"

using namespace adaptative::logic;
using namespace adaptative::playlist;
using namespace adaptative::xml;
using namespace dash::mpd;
using namespace dash;
using namespace hls;
using namespace hls::playlist;
using namespace smooth;
using namespace smooth::playlist;

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open    (vlc_object_t *);
static void Close   (vlc_object_t *);

#define ADAPT_WIDTH_TEXT N_("Preferred Width")

#define ADAPT_HEIGHT_TEXT N_("Preferred Height")

#define ADAPT_BW_TEXT N_("Fixed Bandwidth in KiB/s")
#define ADAPT_BW_LONGTEXT N_("Preferred bandwidth for non adaptative streams")

#define ADAPT_LOGIC_TEXT N_("Adaptation Logic")

static const int pi_logics[] = {AbstractAdaptationLogic::RateBased,
                                AbstractAdaptationLogic::FixedRate,
                                AbstractAdaptationLogic::AlwaysLowest,
                                AbstractAdaptationLogic::AlwaysBest};

static const char *const ppsz_logics[] = { N_("Bandwidth Adaptive"),
                                           N_("Fixed Bandwidth"),
                                           N_("Lowest Bandwidth/Quality"),
                                           N_("Highest Bandwith/Quality")};

vlc_module_begin ()
        set_shortname( N_("Adaptative"))
        set_description( N_("Unified adaptative streaming for DASH/HLS") )
        set_capability( "demux", 12 )
        set_category( CAT_INPUT )
        set_subcategory( SUBCAT_INPUT_DEMUX )
        add_integer( "adaptative-logic",  AbstractAdaptationLogic::Default,
                                          ADAPT_LOGIC_TEXT, NULL, false )
            change_integer_list( pi_logics, ppsz_logics )
        add_integer( "adaptative-width",  480, ADAPT_WIDTH_TEXT,  ADAPT_WIDTH_TEXT,  true )
        add_integer( "adaptative-height", 360, ADAPT_HEIGHT_TEXT, ADAPT_HEIGHT_TEXT, true )
        add_integer( "adaptative-bw",     250, ADAPT_BW_TEXT,     ADAPT_BW_LONGTEXT,     false )
        set_callbacks( Open, Close )
vlc_module_end ()

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static PlaylistManager * HandleDash(demux_t *, DOMParser &,
                                    const std::string &, AbstractAdaptationLogic::LogicType);
static PlaylistManager * HandleSmooth(demux_t *, DOMParser &,
                                      const std::string &, AbstractAdaptationLogic::LogicType);

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open(vlc_object_t *p_obj)
{
    demux_t *p_demux = (demux_t*) p_obj;

    std::string mimeType;

    char *psz_mime = stream_ContentType(p_demux->s);
    if(psz_mime)
    {
        mimeType = std::string(psz_mime);
        free(psz_mime);
    }

    PlaylistManager *p_manager = NULL;
    AbstractAdaptationLogic::LogicType logic =
            static_cast<AbstractAdaptationLogic::LogicType>(var_InheritInteger(p_obj, "adaptative-logic"));

    std::string playlisturl(p_demux->psz_access);
    playlisturl.append("://");
    playlisturl.append(p_demux->psz_location);

    bool dashmime = DASHManager::mimeMatched(mimeType);
    bool smoothmime = SmoothManager::mimeMatched(mimeType);

    if(!dashmime && !smoothmime && HLSManager::isHTTPLiveStreaming(p_demux->s))
    {
        M3U8Parser parser;
        M3U8 *p_playlist = parser.parse(p_demux->s, playlisturl);
        if(!p_playlist)
        {
            msg_Err( p_demux, "Could not parse playlist" );
            return VLC_EGENERIC;
        }

        p_manager = new (std::nothrow) HLSManager(p_demux, p_playlist,
                                                  new (std::nothrow) HLSStreamFactory, logic);
    }
    else
    {
        /* Handle XML Based ones */
        DOMParser xmlParser; /* Share that xml reader */
        if(dashmime)
        {
            p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
        }
        else if(smoothmime)
        {
            p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
        }
        else
        {
            /* We need to probe content */
            const uint8_t *p_peek;
            const size_t i_peek = stream_Peek(p_demux->s, &p_peek, 2048);
            stream_t *peekstream = stream_MemoryNew(p_demux, const_cast<uint8_t *>(p_peek), i_peek, true);
            if(peekstream)
            {
                if(xmlParser.reset(peekstream) && xmlParser.parse(false))
                {
                    if(DASHManager::isDASH(xmlParser.getRootNode()))
                    {
                        p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
                    }
                    else if(SmoothManager::isSmoothStreaming(xmlParser.getRootNode()))
                    {
                        p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
                    }
                }
                stream_Delete(peekstream);
            }
        }
    }

    if(!p_manager || !p_manager->start())
    {
        delete p_manager;
        return VLC_EGENERIC;
    }

    p_demux->p_sys         = reinterpret_cast<demux_sys_t *>(p_manager);
    p_demux->pf_demux      = p_manager->demux_callback;
    p_demux->pf_control    = p_manager->control_callback;

    msg_Dbg(p_obj,"opening playlist file (%s)", p_demux->psz_location);

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close(vlc_object_t *p_obj)
{
    demux_t         *p_demux       = (demux_t*) p_obj;
    PlaylistManager *p_manager  = reinterpret_cast<PlaylistManager *>(p_demux->p_sys);

    delete p_manager;
}

/*****************************************************************************
 *
 *****************************************************************************/
static PlaylistManager * HandleDash(demux_t *p_demux, DOMParser &xmlParser,
                                    const std::string & playlisturl,
                                    AbstractAdaptationLogic::LogicType logic)
{
    if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
    {
        msg_Err(p_demux, "Cannot parse MPD");
        return NULL;
    }
    IsoffMainParser mpdparser(xmlParser.getRootNode(), p_demux->s, playlisturl);
    MPD *p_playlist = mpdparser.parse();
    if(p_playlist == NULL)
    {
        msg_Err( p_demux, "Cannot create/unknown MPD for profile");
        return NULL;
    }

    return new (std::nothrow) DASHManager( p_demux, p_playlist,
                                 new (std::nothrow) DASHStreamFactory,
                                 logic );
}

static PlaylistManager * HandleSmooth(demux_t *p_demux, DOMParser &xmlParser,
                                    const std::string & playlisturl,
                                    AbstractAdaptationLogic::LogicType logic)
{
    if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
    {
        msg_Err(p_demux, "Cannot parse Manifest");
        return NULL;
    }
    ManifestParser mparser(xmlParser.getRootNode(), p_demux->s, playlisturl);
    Manifest *p_playlist = mparser.parse();
    if(p_playlist == NULL)
    {
        msg_Err( p_demux, "Cannot create Manifest");
        return NULL;
    }

    return new (std::nothrow) SmoothManager( p_demux, p_playlist,
                                 new (std::nothrow) SmoothStreamFactory,
                                 logic );
}
<commit_msg>demux: adaptative: handle peek error code (fix #15819)<commit_after>/*****************************************************************************
 * adaptative.cpp: Adaptative streaming module
 *****************************************************************************
 * Copyright © 2015 - VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdint.h>

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>

#include "playlist/BasePeriod.h"
#include "xml/DOMParser.h"

#include "../dash/DASHManager.h"
#include "../dash/DASHStream.hpp"
#include "../dash/mpd/IsoffMainParser.h"

#include "../hls/HLSManager.hpp"
#include "../hls/HLSStreams.hpp"
#include "../hls/playlist/Parser.hpp"
#include "../hls/playlist/M3U8.hpp"
#include "../smooth/SmoothManager.hpp"
#include "../smooth/SmoothStream.hpp"
#include "../smooth/playlist/Parser.hpp"

using namespace adaptative::logic;
using namespace adaptative::playlist;
using namespace adaptative::xml;
using namespace dash::mpd;
using namespace dash;
using namespace hls;
using namespace hls::playlist;
using namespace smooth;
using namespace smooth::playlist;

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open    (vlc_object_t *);
static void Close   (vlc_object_t *);

#define ADAPT_WIDTH_TEXT N_("Preferred Width")

#define ADAPT_HEIGHT_TEXT N_("Preferred Height")

#define ADAPT_BW_TEXT N_("Fixed Bandwidth in KiB/s")
#define ADAPT_BW_LONGTEXT N_("Preferred bandwidth for non adaptative streams")

#define ADAPT_LOGIC_TEXT N_("Adaptation Logic")

static const int pi_logics[] = {AbstractAdaptationLogic::RateBased,
                                AbstractAdaptationLogic::FixedRate,
                                AbstractAdaptationLogic::AlwaysLowest,
                                AbstractAdaptationLogic::AlwaysBest};

static const char *const ppsz_logics[] = { N_("Bandwidth Adaptive"),
                                           N_("Fixed Bandwidth"),
                                           N_("Lowest Bandwidth/Quality"),
                                           N_("Highest Bandwith/Quality")};

vlc_module_begin ()
        set_shortname( N_("Adaptative"))
        set_description( N_("Unified adaptative streaming for DASH/HLS") )
        set_capability( "demux", 12 )
        set_category( CAT_INPUT )
        set_subcategory( SUBCAT_INPUT_DEMUX )
        add_integer( "adaptative-logic",  AbstractAdaptationLogic::Default,
                                          ADAPT_LOGIC_TEXT, NULL, false )
            change_integer_list( pi_logics, ppsz_logics )
        add_integer( "adaptative-width",  480, ADAPT_WIDTH_TEXT,  ADAPT_WIDTH_TEXT,  true )
        add_integer( "adaptative-height", 360, ADAPT_HEIGHT_TEXT, ADAPT_HEIGHT_TEXT, true )
        add_integer( "adaptative-bw",     250, ADAPT_BW_TEXT,     ADAPT_BW_LONGTEXT,     false )
        set_callbacks( Open, Close )
vlc_module_end ()

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static PlaylistManager * HandleDash(demux_t *, DOMParser &,
                                    const std::string &, AbstractAdaptationLogic::LogicType);
static PlaylistManager * HandleSmooth(demux_t *, DOMParser &,
                                      const std::string &, AbstractAdaptationLogic::LogicType);

/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open(vlc_object_t *p_obj)
{
    demux_t *p_demux = (demux_t*) p_obj;

    std::string mimeType;

    char *psz_mime = stream_ContentType(p_demux->s);
    if(psz_mime)
    {
        mimeType = std::string(psz_mime);
        free(psz_mime);
    }

    PlaylistManager *p_manager = NULL;
    AbstractAdaptationLogic::LogicType logic =
            static_cast<AbstractAdaptationLogic::LogicType>(var_InheritInteger(p_obj, "adaptative-logic"));

    std::string playlisturl(p_demux->psz_access);
    playlisturl.append("://");
    playlisturl.append(p_demux->psz_location);

    bool dashmime = DASHManager::mimeMatched(mimeType);
    bool smoothmime = SmoothManager::mimeMatched(mimeType);

    if(!dashmime && !smoothmime && HLSManager::isHTTPLiveStreaming(p_demux->s))
    {
        M3U8Parser parser;
        M3U8 *p_playlist = parser.parse(p_demux->s, playlisturl);
        if(!p_playlist)
        {
            msg_Err( p_demux, "Could not parse playlist" );
            return VLC_EGENERIC;
        }

        p_manager = new (std::nothrow) HLSManager(p_demux, p_playlist,
                                                  new (std::nothrow) HLSStreamFactory, logic);
    }
    else
    {
        /* Handle XML Based ones */
        DOMParser xmlParser; /* Share that xml reader */
        if(dashmime)
        {
            p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
        }
        else if(smoothmime)
        {
            p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
        }
        else
        {
            /* We need to probe content */
            const uint8_t *p_peek;
            const ssize_t i_peek = stream_Peek(p_demux->s, &p_peek, 2048);
            if(i_peek > 0)
            {
                stream_t *peekstream = stream_MemoryNew(p_demux, const_cast<uint8_t *>(p_peek), (size_t)i_peek, true);
                if(peekstream)
                {
                    if(xmlParser.reset(peekstream) && xmlParser.parse(false))
                    {
                        if(DASHManager::isDASH(xmlParser.getRootNode()))
                        {
                            p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
                        }
                        else if(SmoothManager::isSmoothStreaming(xmlParser.getRootNode()))
                        {
                            p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
                        }
                    }
                    stream_Delete(peekstream);
                }
            }
        }
    }

    if(!p_manager || !p_manager->start())
    {
        delete p_manager;
        return VLC_EGENERIC;
    }

    p_demux->p_sys         = reinterpret_cast<demux_sys_t *>(p_manager);
    p_demux->pf_demux      = p_manager->demux_callback;
    p_demux->pf_control    = p_manager->control_callback;

    msg_Dbg(p_obj,"opening playlist file (%s)", p_demux->psz_location);

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close(vlc_object_t *p_obj)
{
    demux_t         *p_demux       = (demux_t*) p_obj;
    PlaylistManager *p_manager  = reinterpret_cast<PlaylistManager *>(p_demux->p_sys);

    delete p_manager;
}

/*****************************************************************************
 *
 *****************************************************************************/
static PlaylistManager * HandleDash(demux_t *p_demux, DOMParser &xmlParser,
                                    const std::string & playlisturl,
                                    AbstractAdaptationLogic::LogicType logic)
{
    if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
    {
        msg_Err(p_demux, "Cannot parse MPD");
        return NULL;
    }
    IsoffMainParser mpdparser(xmlParser.getRootNode(), p_demux->s, playlisturl);
    MPD *p_playlist = mpdparser.parse();
    if(p_playlist == NULL)
    {
        msg_Err( p_demux, "Cannot create/unknown MPD for profile");
        return NULL;
    }

    return new (std::nothrow) DASHManager( p_demux, p_playlist,
                                 new (std::nothrow) DASHStreamFactory,
                                 logic );
}

static PlaylistManager * HandleSmooth(demux_t *p_demux, DOMParser &xmlParser,
                                    const std::string & playlisturl,
                                    AbstractAdaptationLogic::LogicType logic)
{
    if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
    {
        msg_Err(p_demux, "Cannot parse Manifest");
        return NULL;
    }
    ManifestParser mparser(xmlParser.getRootNode(), p_demux->s, playlisturl);
    Manifest *p_playlist = mparser.parse();
    if(p_playlist == NULL)
    {
        msg_Err( p_demux, "Cannot create Manifest");
        return NULL;
    }

    return new (std::nothrow) SmoothManager( p_demux, p_playlist,
                                 new (std::nothrow) SmoothStreamFactory,
                                 logic );
}
<|endoftext|>


<commit_before>// ======================================================================== //
// Copyright 2009-2017 Intel Corporation                                    //
//                                                                          //
// 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 "VolumeSceneParser.h"

#include <ospray/ospray_cpp/Data.h>

using namespace ospray;
using namespace ospcommon;

#include "common/importer/Importer.h"
#include "common/tfn_lib/tfn_lib.h"

#include <iostream>
using std::cerr;
using std::endl;

#include <cstdlib>

// SceneParser definitions ////////////////////////////////////////////////////

VolumeSceneParser::VolumeSceneParser(cpp::Renderer renderer) :
  renderer(renderer)
{
}

bool VolumeSceneParser::parse(int ac, const char **&av)
{
  bool loadedScene = false;
  bool loadedTransferFunction = false;

  FileName scene;

  for (int i = 1; i < ac; i++) {
    const std::string arg = av[i];
    if (arg == "-s" || arg == "--sampling-rate") {
      samplingRate = atof(av[++i]);
    } else if (arg == "-tfc" || arg == "--tf-color") {
      ospcommon::vec4f color;
      color.x = atof(av[++i]);
      color.y = atof(av[++i]);
      color.z = atof(av[++i]);
      color.w = atof(av[++i]);
      tf_colors.push_back(color);
    } else if (arg == "-tfs" || arg == "--tf-scale") {
      tf_scale = atof(av[++i]);
    } else if (arg == "-tff" || arg == "--tf-file") {
      importTransferFunction(std::string(av[++i]));
      loadedTransferFunction = true;
    } else if (arg == "-is" || arg == "--surface") {
      isosurfaces.push_back(atof(av[++i]));
    } else {
      FileName fn = arg;
      if (fn.ext() == "osp") {
        scene = arg;
        loadedScene = true;
      }
    }
  }

  if (loadedScene) {
    sceneModel = make_unique<cpp::Model>();
    if (!loadedTransferFunction) {
      createDefaultTransferFunction();
    }
    importObjectsFromFile(scene, loadedTransferFunction);
  }

  return loadedScene;
}

std::deque<cpp::Model> VolumeSceneParser::model() const
{
  std::deque<cpp::Model> models;
  models.push_back(sceneModel == nullptr ? cpp::Model() : *sceneModel);
  return models;
  // return sceneModel.get() == nullptr ? cpp::Model() : *sceneModel;
}

std::deque<box3f> VolumeSceneParser::bbox() const
{
  std::deque<ospcommon::box3f> boxes;
  boxes.push_back(sceneBbox);
  return boxes;
}

void VolumeSceneParser::importObjectsFromFile(const std::string &filename,
                                              bool loadedTransferFunction)
{
  auto &model = *sceneModel;

  // Load OSPRay objects from a file.
  ospray::importer::Group *imported = ospray::importer::import(filename);

  // Iterate over geometries
  for (size_t i = 0; i < imported->geometry.size(); i++) {
    auto geometry = ospray::cpp::Geometry(imported->geometry[i]->handle);
    geometry.commit();
    model.addGeometry(geometry);
  }

  // Iterate over volumes
  for (size_t i = 0 ; i < imported->volume.size(); i++) {
    ospray::importer::Volume *vol = imported->volume[i];
    auto volume = ospray::cpp::Volume(vol->handle);

    // For now we set the same transfer function on all volumes.
    volume.set("transferFunction", transferFunction);
    volume.set("samplingRate", samplingRate);
    volume.commit();

    // Add the loaded volume(s) to the model.
    model.addVolume(volume);

    // Set the minimum and maximum values in the domain for both color and
    // opacity components of the transfer function if we didn't load a transfer
    // function for a file (in that case this is already set)
    if (!loadedTransferFunction) {
      transferFunction.set("valueRange", vol->voxelRange.x, vol->voxelRange.y);
      transferFunction.commit();
    }

    //sceneBbox.extend(vol->bounds);
    sceneBbox = vol->bounds;

    // Create any specified isosurfaces
    if (!isosurfaces.empty()) {
      auto isoValueData = ospray::cpp::Data(isosurfaces.size(), OSP_FLOAT,
                                            isosurfaces.data());
      auto isoGeometry = ospray::cpp::Geometry("isosurfaces");

      isoGeometry.set("isovalues", isoValueData);
      isoGeometry.set("volume", volume);
      isoGeometry.commit();

      model.addGeometry(isoGeometry);
    }
  }

  model.commit();
}

void VolumeSceneParser::importTransferFunction(const std::string &filename)
{
  tfn::TransferFunction fcn(filename);
  auto colorsData = ospray::cpp::Data(fcn.rgbValues.size(), OSP_FLOAT3,
                                      fcn.rgbValues.data());
  transferFunction = cpp::TransferFunction("piecewise_linear");
  transferFunction.set("colors", colorsData);

  tf_scale = fcn.opacityScaling;
  // Sample the opacity values, taking 256 samples to match the volume viewer
  // the volume viewer does the sampling a bit differently so we match that
  // instead of what's done in createDefault
  std::vector<float> opacityValues;
  const int N_OPACITIES = 256;
  size_t lo = 0;
  size_t hi = 1;
  for (int i = 0; i < N_OPACITIES; ++i) {
    const float x = float(i) / float(N_OPACITIES - 1);
    float opacity = 0;
    if (i == 0) {
      opacity = fcn.opacityValues[0].y;
    } else if (i == N_OPACITIES - 1) {
      opacity = fcn.opacityValues.back().y;
    } else {
      // If we're over this val, find the next segment
      if (x > fcn.opacityValues[lo].x) {
        for (size_t j = lo; j < fcn.opacityValues.size() - 1; ++j) {
          if (x <= fcn.opacityValues[j + 1].x) {
            lo = j;
            hi = j + 1;
            break;
          }
        }
      }
      const float delta = x - fcn.opacityValues[lo].x;
      const float interval = fcn.opacityValues[hi].x - fcn.opacityValues[lo].x;
      if (delta == 0 || interval == 0) {
        opacity = fcn.opacityValues[lo].y;
      } else {
        opacity = fcn.opacityValues[lo].y + delta / interval
          * (fcn.opacityValues[hi].y - fcn.opacityValues[lo].y);
      }
    }
    opacityValues.push_back(tf_scale * opacity);
  }

  auto opacityValuesData = ospray::cpp::Data(opacityValues.size(),
                                             OSP_FLOAT,
                                             opacityValues.data());
  transferFunction.set("opacities", opacityValuesData);
  transferFunction.set("valueRange", vec2f(fcn.dataValueMin, fcn.dataValueMax));

  // Commit transfer function
  transferFunction.commit();
}
void VolumeSceneParser::createDefaultTransferFunction()
{
  transferFunction = cpp::TransferFunction("piecewise_linear");

  // Add colors
  std::vector<vec4f> colors;
  if (tf_colors.empty()) {
    colors.emplace_back(0.f, 0.f, 0.f, 0.f);
    colors.emplace_back(0.9f, 0.9f, 0.9f, 1.f);
  } else {
    colors = tf_colors;
  }
  std::vector<vec3f> colorsAsVec3;
  for (auto &c : colors) colorsAsVec3.emplace_back(c.x, c.y, c.z);
  auto colorsData = ospray::cpp::Data(colors.size(), OSP_FLOAT3,
                                      colorsAsVec3.data());
  transferFunction.set("colors", colorsData);

  // Add opacities
  std::vector<float> opacityValues;

  const int N_OPACITIES = 64;//NOTE(jda) - This affects image quality and
                             //            performance!
  const int N_INTERVALS = colors.size() - 1;
  const float OPACITIES_PER_INTERVAL = N_OPACITIES / float(N_INTERVALS);
  for (int i = 0; i < N_OPACITIES; ++i) {
    int lcolor = static_cast<int>(i/OPACITIES_PER_INTERVAL);
    int hcolor = lcolor + 1;

    float v0 = colors[lcolor].w;
    float v1 = colors[hcolor].w;
    float t = (i / OPACITIES_PER_INTERVAL) - lcolor;

    float opacity = (1-t)*v0 + t*v1;
    if (opacity > 1.f) opacity = 1.f;
    opacityValues.push_back(tf_scale*opacity);
  }
  auto opacityValuesData = ospray::cpp::Data(opacityValues.size(),
                                             OSP_FLOAT,
                                             opacityValues.data());
  transferFunction.set("opacities", opacityValuesData);

  // Commit transfer function
  transferFunction.commit();
}
<commit_msg>add ability to override TF type using env var in VolumeSceneParser<commit_after>// ======================================================================== //
// Copyright 2009-2017 Intel Corporation                                    //
//                                                                          //
// 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 "VolumeSceneParser.h"

#include <ospray/ospray_cpp/Data.h>

using namespace ospray;
using namespace ospcommon;

#include "common/importer/Importer.h"
#include "common/tfn_lib/tfn_lib.h"

#include <iostream>
using std::cerr;
using std::endl;

#include <cstdlib>

// SceneParser definitions ////////////////////////////////////////////////////

VolumeSceneParser::VolumeSceneParser(cpp::Renderer renderer) :
  renderer(renderer)
{
}

bool VolumeSceneParser::parse(int ac, const char **&av)
{
  bool loadedScene = false;
  bool loadedTransferFunction = false;

  FileName scene;

  for (int i = 1; i < ac; i++) {
    const std::string arg = av[i];
    if (arg == "-s" || arg == "--sampling-rate") {
      samplingRate = atof(av[++i]);
    } else if (arg == "-tfc" || arg == "--tf-color") {
      ospcommon::vec4f color;
      color.x = atof(av[++i]);
      color.y = atof(av[++i]);
      color.z = atof(av[++i]);
      color.w = atof(av[++i]);
      tf_colors.push_back(color);
    } else if (arg == "-tfs" || arg == "--tf-scale") {
      tf_scale = atof(av[++i]);
    } else if (arg == "-tff" || arg == "--tf-file") {
      importTransferFunction(std::string(av[++i]));
      loadedTransferFunction = true;
    } else if (arg == "-is" || arg == "--surface") {
      isosurfaces.push_back(atof(av[++i]));
    } else {
      FileName fn = arg;
      if (fn.ext() == "osp") {
        scene = arg;
        loadedScene = true;
      }
    }
  }

  if (loadedScene) {
    sceneModel = make_unique<cpp::Model>();
    if (!loadedTransferFunction) {
      createDefaultTransferFunction();
    }
    importObjectsFromFile(scene, loadedTransferFunction);
  }

  return loadedScene;
}

std::deque<cpp::Model> VolumeSceneParser::model() const
{
  std::deque<cpp::Model> models;
  models.push_back(sceneModel == nullptr ? cpp::Model() : *sceneModel);
  return models;
  // return sceneModel.get() == nullptr ? cpp::Model() : *sceneModel;
}

std::deque<box3f> VolumeSceneParser::bbox() const
{
  std::deque<ospcommon::box3f> boxes;
  boxes.push_back(sceneBbox);
  return boxes;
}

void VolumeSceneParser::importObjectsFromFile(const std::string &filename,
                                              bool loadedTransferFunction)
{
  auto &model = *sceneModel;

  // Load OSPRay objects from a file.
  ospray::importer::Group *imported = ospray::importer::import(filename);

  // Iterate over geometries
  for (size_t i = 0; i < imported->geometry.size(); i++) {
    auto geometry = ospray::cpp::Geometry(imported->geometry[i]->handle);
    geometry.commit();
    model.addGeometry(geometry);
  }

  // Iterate over volumes
  for (size_t i = 0 ; i < imported->volume.size(); i++) {
    ospray::importer::Volume *vol = imported->volume[i];
    auto volume = ospray::cpp::Volume(vol->handle);

    // For now we set the same transfer function on all volumes.
    volume.set("transferFunction", transferFunction);
    volume.set("samplingRate", samplingRate);
    volume.commit();

    // Add the loaded volume(s) to the model.
    model.addVolume(volume);

    // Set the minimum and maximum values in the domain for both color and
    // opacity components of the transfer function if we didn't load a transfer
    // function for a file (in that case this is already set)
    if (!loadedTransferFunction) {
      transferFunction.set("valueRange", vol->voxelRange.x, vol->voxelRange.y);
      transferFunction.commit();
    }

    //sceneBbox.extend(vol->bounds);
    sceneBbox = vol->bounds;

    // Create any specified isosurfaces
    if (!isosurfaces.empty()) {
      auto isoValueData = ospray::cpp::Data(isosurfaces.size(), OSP_FLOAT,
                                            isosurfaces.data());
      auto isoGeometry = ospray::cpp::Geometry("isosurfaces");

      isoGeometry.set("isovalues", isoValueData);
      isoGeometry.set("volume", volume);
      isoGeometry.commit();

      model.addGeometry(isoGeometry);
    }
  }

  model.commit();
}

void VolumeSceneParser::importTransferFunction(const std::string &filename)
{
  tfn::TransferFunction fcn(filename);
  auto colorsData = ospray::cpp::Data(fcn.rgbValues.size(), OSP_FLOAT3,
                                      fcn.rgbValues.data());
  auto tfFromEnv = getEnvVar<std::string>("OSPRAY_USE_TF_TYPE");

  if (tfFromEnv.first) {
    transferFunction = cpp::TransferFunction(tfFromEnv.second);
  } else {
    transferFunction = cpp::TransferFunction("piecewise_linear");
  }
  transferFunction.set("colors", colorsData);

  tf_scale = fcn.opacityScaling;
  // Sample the opacity values, taking 256 samples to match the volume viewer
  // the volume viewer does the sampling a bit differently so we match that
  // instead of what's done in createDefault
  std::vector<float> opacityValues;
  const int N_OPACITIES = 256;
  size_t lo = 0;
  size_t hi = 1;
  for (int i = 0; i < N_OPACITIES; ++i) {
    const float x = float(i) / float(N_OPACITIES - 1);
    float opacity = 0;
    if (i == 0) {
      opacity = fcn.opacityValues[0].y;
    } else if (i == N_OPACITIES - 1) {
      opacity = fcn.opacityValues.back().y;
    } else {
      // If we're over this val, find the next segment
      if (x > fcn.opacityValues[lo].x) {
        for (size_t j = lo; j < fcn.opacityValues.size() - 1; ++j) {
          if (x <= fcn.opacityValues[j + 1].x) {
            lo = j;
            hi = j + 1;
            break;
          }
        }
      }
      const float delta = x - fcn.opacityValues[lo].x;
      const float interval = fcn.opacityValues[hi].x - fcn.opacityValues[lo].x;
      if (delta == 0 || interval == 0) {
        opacity = fcn.opacityValues[lo].y;
      } else {
        opacity = fcn.opacityValues[lo].y + delta / interval
          * (fcn.opacityValues[hi].y - fcn.opacityValues[lo].y);
      }
    }
    opacityValues.push_back(tf_scale * opacity);
  }

  auto opacityValuesData = ospray::cpp::Data(opacityValues.size(),
                                             OSP_FLOAT,
                                             opacityValues.data());
  transferFunction.set("opacities", opacityValuesData);
  transferFunction.set("valueRange", vec2f(fcn.dataValueMin, fcn.dataValueMax));

  // Commit transfer function
  transferFunction.commit();
}
void VolumeSceneParser::createDefaultTransferFunction()
{
  auto tfFromEnv = getEnvVar<std::string>("OSPRAY_USE_TF_TYPE");

  if (tfFromEnv.first) {
    transferFunction = cpp::TransferFunction(tfFromEnv.second);
  } else {
    transferFunction = cpp::TransferFunction("piecewise_linear");
  }

  // Add colors
  std::vector<vec4f> colors;
  if (tf_colors.empty()) {
    colors.emplace_back(0.f, 0.f, 0.f, 0.f);
    colors.emplace_back(0.9f, 0.9f, 0.9f, 1.f);
  } else {
    colors = tf_colors;
  }
  std::vector<vec3f> colorsAsVec3;
  for (auto &c : colors) colorsAsVec3.emplace_back(c.x, c.y, c.z);
  auto colorsData = ospray::cpp::Data(colors.size(), OSP_FLOAT3,
                                      colorsAsVec3.data());
  transferFunction.set("colors", colorsData);

  // Add opacities
  std::vector<float> opacityValues;

  const int N_OPACITIES = 64;//NOTE(jda) - This affects image quality and
                             //            performance!
  const int N_INTERVALS = colors.size() - 1;
  const float OPACITIES_PER_INTERVAL = N_OPACITIES / float(N_INTERVALS);
  for (int i = 0; i < N_OPACITIES; ++i) {
    int lcolor = static_cast<int>(i/OPACITIES_PER_INTERVAL);
    int hcolor = lcolor + 1;

    float v0 = colors[lcolor].w;
    float v1 = colors[hcolor].w;
    float t = (i / OPACITIES_PER_INTERVAL) - lcolor;

    float opacity = (1-t)*v0 + t*v1;
    if (opacity > 1.f) opacity = 1.f;
    opacityValues.push_back(tf_scale*opacity);
  }
  auto opacityValuesData = ospray::cpp::Data(opacityValues.size(),
                                             OSP_FLOAT,
                                             opacityValues.data());
  transferFunction.set("opacities", opacityValuesData);

  // Commit transfer function
  transferFunction.commit();
}
<|endoftext|>


<commit_before>#include "audio/audio.hpp"
#include "main/main.hpp"
#include "world/world.hpp"
#include "graphics/graphics.hpp"

namespace Polarity {

using namespace std;

AudioFile *audioTest;
AudioChannelPlayer *audioPlayer;
bool buzzing = false;       // TODO: bring this into a game state! you should buzz if you have the appropriate powerup

bool loaded = false;

shared_ptr<Image> test_image;

void loadAssets() {
    audioPlayer = new AudioChannelPlayer(32);
    if (audioPlayer->addChannel("white", "assets/audio/frozen_star.mp3", 0) != AudioFileError::OK) {
        std::cerr << "Couldn't load white track" << std::endl;
    }
    if (audioPlayer->addChannel("black", "assets/audio/lightless_dawn.mp3", 1) != AudioFileError::OK) {
        std::cerr << "Couldn't load black track" << std::endl;
    }
    if (audioPlayer->addChannel("buzz", "assets/audio/buzz.mp3", 2) != AudioFileError::OK) {
        std::cerr << "Couldn't load buzz track" << std::endl;
    } else {
        audioPlayer->setChannelVolume("buzz", 1.0);
    }
    test_image = Image::get("assets/helloworld.png");
}

bool loopIter(SDL_Surface *screen) {
    test_image->draw(screen, 0, 0);

    SDL_Event event;
    std::vector<int> keyUps;
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
            if (event.type == SDL_KEYDOWN) {
                world->keyEvent(event.key.keysym.sym, true);
                if (event.key.keysym.sym == SDLK_SPACE) {
                    audioPlayer->playChannel("white");
                    audioPlayer->playChannel("black");
                } else if (event.key.keysym.sym == SDLK_ESCAPE) {
                    audioPlayer->stopChannel("white");
                    audioPlayer->stopChannel("black");
                } else if (event.key.keysym.sym == SDLK_w) {
                    // switch to white track
                    audioPlayer->setChannelVolume("white", 1.0);
                    audioPlayer->setChannelVolume("black", 0.0);
                } else if (event.key.keysym.sym == SDLK_b) {
                    // switch to black track
                    audioPlayer->setChannelVolume("white", 0.0);
                    audioPlayer->setChannelVolume("black", 1.0);
                } else if (event.key.keysym.sym == SDLK_1) {
                    if (!buzzing) {
                        audioPlayer->playChannel("buzz", -1);
                    } else {
                        audioPlayer->stopChannel("buzz");
                    }
                    buzzing = !buzzing;
                } else if (event.key.keysym.sym == SDLK_2) {

                }
            } else {
                keyUps.push_back(event.key.keysym.sym);
            }
        }
    }
    world->tick();
    world->draw(screen);
    // all key up have to happen after key downs so we get a full tick of downs
    for (auto &key : keyUps) {
      world->keyEvent(key, false);
    }
    return true;
}
}
<commit_msg>get rid of the changes in loop<commit_after>#include "audio/audio.hpp"
#include "main/main.hpp"
#include "world/world.hpp"
#include "graphics/graphics.hpp"

namespace Polarity {

using namespace std;

AudioFile *audioTest;
AudioChannelPlayer *audioPlayer;
bool buzzing = false;       // TODO: bring this into a game state! you should buzz if you have the appropriate powerup

bool loaded = false;

void loadAssets() {
    audioPlayer = new AudioChannelPlayer(32);
    if (audioPlayer->addChannel("white", "assets/audio/frozen_star.mp3", 0) != AudioFileError::OK) {
        std::cerr << "Couldn't load white track" << std::endl;
    }
    if (audioPlayer->addChannel("black", "assets/audio/lightless_dawn.mp3", 1) != AudioFileError::OK) {
        std::cerr << "Couldn't load black track" << std::endl;
    }
    if (audioPlayer->addChannel("buzz", "assets/audio/buzz.mp3", 2) != AudioFileError::OK) {
        std::cerr << "Couldn't load buzz track" << std::endl;
    } else {
        audioPlayer->setChannelVolume("buzz", 1.0);
    }
}

bool loopIter(SDL_Surface *screen) {
    SDL_Event event;
    std::vector<int> keyUps;
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
            if (event.type == SDL_KEYDOWN) {
                world->keyEvent(event.key.keysym.sym, true);
                if (event.key.keysym.sym == SDLK_SPACE) {
                    audioPlayer->playChannel("white");
                    audioPlayer->playChannel("black");
                } else if (event.key.keysym.sym == SDLK_ESCAPE) {
                    audioPlayer->stopChannel("white");
                    audioPlayer->stopChannel("black");
                } else if (event.key.keysym.sym == SDLK_w) {
                    // switch to white track
                    audioPlayer->setChannelVolume("white", 1.0);
                    audioPlayer->setChannelVolume("black", 0.0);
                } else if (event.key.keysym.sym == SDLK_b) {
                    // switch to black track
                    audioPlayer->setChannelVolume("white", 0.0);
                    audioPlayer->setChannelVolume("black", 1.0);
                } else if (event.key.keysym.sym == SDLK_1) {
                    if (!buzzing) {
                        audioPlayer->playChannel("buzz", -1);
                    } else {
                        audioPlayer->stopChannel("buzz");
                    }
                    buzzing = !buzzing;
                } else if (event.key.keysym.sym == SDLK_2) {

                }
            } else {
                keyUps.push_back(event.key.keysym.sym);
            }
        }
    }
    world->tick();
    world->draw(screen);
    // all key up have to happen after key downs so we get a full tick of downs
    for (auto &key : keyUps) {
      world->keyEvent(key, false);
    }
    return true;
}
}
<|endoftext|>


<commit_before>// @(#)root/gl:$Id$
// Author:  Timur Pocheptsov  03/08/2004
// NOTE: This code moved from obsoleted TGLSceneObject.h / .cxx - see these
// attic files for previous CVS history

/*************************************************************************
 * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TGLFaceSet.h"
#include "TGLRnrCtx.h"
#include "TGLIncludes.h"

#include "TBuffer3D.h"
#include "TMath.h"

// For debug tracing
#include "TClass.h"
#include "TError.h"

//______________________________________________________________________________
//
// Implementss a native ROOT-GL representation of an arbitrary set of
// polygons.

ClassImp(TGLFaceSet);

//______________________________________________________________________________
TGLFaceSet::TGLFaceSet(const TBuffer3D & buffer) :
   TGLLogicalShape(buffer),
   fVertices(buffer.fPnts, buffer.fPnts + 3 * buffer.NbPnts()),
   fNormals(3 * buffer.NbPols())
{
   // constructor
   fNbPols = buffer.NbPols();

   Int_t *segs = buffer.fSegs;
   Int_t *pols = buffer.fPols;

   Int_t descSize = 0;

   for (UInt_t i = 0, j = 1; i < fNbPols; ++i, ++j)
   {
      descSize += pols[j] + 1;
      j += pols[j] + 1;
   }

   fPolyDesc.resize(descSize);
   {//fix for scope
   for (UInt_t numPol = 0, currInd = 0, j = 1; numPol < fNbPols; ++numPol) {
      Int_t segmentInd = pols[j] + j;
      Int_t segmentCol = pols[j];
      Int_t s1 = pols[segmentInd];
      segmentInd--;
      Int_t s2 = pols[segmentInd];
      segmentInd--;
      Int_t segEnds[] = {segs[s1 * 3 + 1], segs[s1 * 3 + 2],
                         segs[s2 * 3 + 1], segs[s2 * 3 + 2]};
      Int_t numPnts[3] = {0};

      if (segEnds[0] == segEnds[2]) {
         numPnts[0] = segEnds[1], numPnts[1] = segEnds[0], numPnts[2] = segEnds[3];
      } else if (segEnds[0] == segEnds[3]) {
         numPnts[0] = segEnds[1], numPnts[1] = segEnds[0], numPnts[2] = segEnds[2];
      } else if (segEnds[1] == segEnds[2]) {
         numPnts[0] = segEnds[0], numPnts[1] = segEnds[1], numPnts[2] = segEnds[3];
      } else {
         numPnts[0] = segEnds[0], numPnts[1] = segEnds[1], numPnts[2] = segEnds[2];
      }

      fPolyDesc[currInd] = 3;
      Int_t sizeInd = currInd++;
      fPolyDesc[currInd++] = numPnts[0];
      fPolyDesc[currInd++] = numPnts[1];
      fPolyDesc[currInd++] = numPnts[2];
      Int_t lastAdded = numPnts[2];

      Int_t end = j + 1;
      for (; segmentInd != end; segmentInd--) {
         segEnds[0] = segs[pols[segmentInd] * 3 + 1];
         segEnds[1] = segs[pols[segmentInd] * 3 + 2];
         if (segEnds[0] == lastAdded) {
            fPolyDesc[currInd++] = segEnds[1];
            lastAdded = segEnds[1];
         } else {
            fPolyDesc[currInd++] = segEnds[0];
            lastAdded = segEnds[0];
         }
         ++fPolyDesc[sizeInd];
      }
      j += segmentCol + 2;
   }
   }
   CalculateNormals();

}

//______________________________________________________________________________
void TGLFaceSet::SetFromMesh(const RootCsg::TBaseMesh *mesh)
{
   // Should only be done on an empty faceset object
   assert(fNbPols == 0);

   UInt_t nv = mesh->NumberOfVertices();
   fVertices.reserve(3 * nv);
   fNormals.resize(mesh->NumberOfPolys() * 3);
   UInt_t i;

   for (i = 0; i < nv; ++i) {
      const Double_t *v = mesh->GetVertex(i);
      fVertices.insert(fVertices.end(), v, v + 3);
   }

   fNbPols = mesh->NumberOfPolys();

   UInt_t descSize = 0;

   for (i = 0; i < fNbPols; ++i) descSize += mesh->SizeOfPoly(i) + 1;

   fPolyDesc.reserve(descSize);

   for (UInt_t polyIndex = 0; polyIndex < fNbPols; ++polyIndex) {
      UInt_t polySize = mesh->SizeOfPoly(polyIndex);

      fPolyDesc.push_back(polySize);

      for(i = 0; i < polySize; ++i) fPolyDesc.push_back(mesh->GetVertexIndex(polyIndex, i));
   }

   CalculateNormals();
}

//______________________________________________________________________________
void TGLFaceSet::DirectDraw(TGLRnrCtx & rnrCtx) const
{
   // Debug tracing
   if (gDebug > 4) {
      Info("TGLFaceSet::DirectDraw", "this %d (class %s) LOD %d", this, IsA()->GetName(), rnrCtx.ShapeLOD());
   }

   GLUtesselator  *tessObj = TGLUtil::GetDrawTesselator3dv();
   const Double_t *pnts = &fVertices[0];
   const Double_t *normals = &fNormals[0];
   const Int_t *pols = &fPolyDesc[0];

   for (UInt_t i = 0, j = 0; i < fNbPols; ++i) {
      Int_t npoints = pols[j++];

      if (tessObj && npoints > 4) {
         gluBeginPolygon(tessObj);
         gluNextContour(tessObj, (GLenum)GLU_UNKNOWN);
         glNormal3dv(normals + i * 3);

         for (Int_t k = 0; k < npoints; ++k, ++j) {
            gluTessVertex(tessObj, (Double_t *)pnts + pols[j] * 3, (Double_t *)pnts + pols[j] * 3);
         }
         gluEndPolygon(tessObj);
      } else {
         glBegin(GL_POLYGON);
         glNormal3dv(normals + i * 3);

         for (Int_t k = 0; k < npoints; ++k, ++j) {
            glVertex3dv(pnts + pols[j] * 3);
         }
         glEnd();
      }
   }
}

//______________________________________________________________________________
Int_t TGLFaceSet::CheckPoints(const Int_t *source, Int_t *dest) const
{
   // CheckPoints
   const Double_t * p1 = &fVertices[source[0] * 3];
   const Double_t * p2 = &fVertices[source[1] * 3];
   const Double_t * p3 = &fVertices[source[2] * 3];
   Int_t retVal = 1;

   if (Eq(p1, p2)) {
      dest[0] = source[0];
      if (!Eq(p1, p3) ) {
         dest[1] = source[2];
         retVal = 2;
      }
   } else if (Eq(p1, p3)) {
      dest[0] = source[0];
      dest[1] = source[1];
      retVal = 2;
   } else {
      dest[0] = source[0];
      dest[1] = source[1];
      retVal = 2;
      if (!Eq(p2, p3)) {
         dest[2] = source[2];
         retVal = 3;
      }
   }

   return retVal;
}

//______________________________________________________________________________
Bool_t TGLFaceSet::Eq(const Double_t *p1, const Double_t *p2)
{
   // test equality
   Double_t dx = TMath::Abs(p1[0] - p2[0]);
   Double_t dy = TMath::Abs(p1[1] - p2[1]);
   Double_t dz = TMath::Abs(p1[2] - p2[2]);
   return dx < 1e-10 && dy < 1e-10 && dz < 1e-10;
}

//______________________________________________________________________________
void TGLFaceSet::CalculateNormals()
{
   // CalculateNormals
   Double_t *pnts = &fVertices[0];
   for (UInt_t i = 0, j = 0; i < fNbPols; ++i) {
      Int_t polEnd = fPolyDesc[j] + j + 1;
      Int_t norm[] = {fPolyDesc[j + 1], fPolyDesc[j + 2], fPolyDesc[j + 3]};
      j += 4;
      Int_t check = CheckPoints(norm, norm), ngood = check;
      if (check == 3) {
         TMath::Normal2Plane(pnts + norm[0] * 3, pnts + norm[1] * 3,
                             pnts + norm[2] * 3, &fNormals[i * 3]);
         j = polEnd;
         continue;
      }
      while (j < (UInt_t)polEnd) {
         norm[ngood++] = fPolyDesc[j++];
         if (ngood == 3) {
            ngood = CheckPoints(norm, norm);
            if (ngood == 3) {
               TMath::Normal2Plane(pnts + norm[0] * 3, pnts + norm[1] * 3,
                                   pnts + norm[2] * 3, &fNormals[i * 3]);
               j = polEnd;
               break;
            }
         }
      }
   }
}
<commit_msg>From Bertrand.<commit_after>// @(#)root/gl:$Id$
// Author:  Timur Pocheptsov  03/08/2004
// NOTE: This code moved from obsoleted TGLSceneObject.h / .cxx - see these
// attic files for previous CVS history

/*************************************************************************
 * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TGLFaceSet.h"
#include "TGLRnrCtx.h"
#include "TGLIncludes.h"

#include "TBuffer3D.h"
#include "TMath.h"

// For debug tracing
#include "TClass.h"
#include "TError.h"

//______________________________________________________________________________
//
// Implementss a native ROOT-GL representation of an arbitrary set of
// polygons.

ClassImp(TGLFaceSet);

//______________________________________________________________________________
TGLFaceSet::TGLFaceSet(const TBuffer3D & buffer) :
   TGLLogicalShape(buffer),
   fVertices(buffer.fPnts, buffer.fPnts + 3 * buffer.NbPnts()),
   fNormals(3 * buffer.NbPols())
{
   // constructor
   fNbPols = buffer.NbPols();

   Int_t *segs = buffer.fSegs;
   Int_t *pols = buffer.fPols;

   Int_t descSize = 0;

   for (UInt_t i = 0, j = 1; i < fNbPols; ++i, ++j)
   {
      descSize += pols[j] + 1;
      j += pols[j] + 1;
   }

   fPolyDesc.resize(descSize);
   {//fix for scope
   for (UInt_t numPol = 0, currInd = 0, j = 1; numPol < fNbPols; ++numPol) {
      Int_t segmentInd = pols[j] + j;
      Int_t segmentCol = pols[j];
      Int_t s1 = pols[segmentInd];
      segmentInd--;
      Int_t s2 = pols[segmentInd];
      segmentInd--;
      Int_t segEnds[] = {segs[s1 * 3 + 1], segs[s1 * 3 + 2],
                         segs[s2 * 3 + 1], segs[s2 * 3 + 2]};
      Int_t numPnts[3] = {0};

      if (segEnds[0] == segEnds[2]) {
         numPnts[0] = segEnds[1], numPnts[1] = segEnds[0], numPnts[2] = segEnds[3];
      } else if (segEnds[0] == segEnds[3]) {
         numPnts[0] = segEnds[1], numPnts[1] = segEnds[0], numPnts[2] = segEnds[2];
      } else if (segEnds[1] == segEnds[2]) {
         numPnts[0] = segEnds[0], numPnts[1] = segEnds[1], numPnts[2] = segEnds[3];
      } else {
         numPnts[0] = segEnds[0], numPnts[1] = segEnds[1], numPnts[2] = segEnds[2];
      }

      fPolyDesc[currInd] = 3;
      Int_t sizeInd = currInd++;
      fPolyDesc[currInd++] = numPnts[0];
      fPolyDesc[currInd++] = numPnts[1];
      fPolyDesc[currInd++] = numPnts[2];
      Int_t lastAdded = numPnts[2];

      Int_t end = j + 1;
      for (; segmentInd != end; segmentInd--) {
         segEnds[0] = segs[pols[segmentInd] * 3 + 1];
         segEnds[1] = segs[pols[segmentInd] * 3 + 2];
         if (segEnds[0] == lastAdded) {
            fPolyDesc[currInd++] = segEnds[1];
            lastAdded = segEnds[1];
         } else {
            fPolyDesc[currInd++] = segEnds[0];
            lastAdded = segEnds[0];
         }
         ++fPolyDesc[sizeInd];
      }
      j += segmentCol + 2;
   }
   }
   CalculateNormals();

}

//______________________________________________________________________________
void TGLFaceSet::SetFromMesh(const RootCsg::TBaseMesh *mesh)
{
   // Should only be done on an empty faceset object
   assert(fNbPols == 0);

   UInt_t nv = mesh->NumberOfVertices();
   fVertices.reserve(3 * nv);
   fNormals.resize(mesh->NumberOfPolys() * 3);
   UInt_t i;

   for (i = 0; i < nv; ++i) {
      const Double_t *v = mesh->GetVertex(i);
      fVertices.insert(fVertices.end(), v, v + 3);
   }

   fNbPols = mesh->NumberOfPolys();

   UInt_t descSize = 0;

   for (i = 0; i < fNbPols; ++i) descSize += mesh->SizeOfPoly(i) + 1;

   fPolyDesc.reserve(descSize);

   for (UInt_t polyIndex = 0; polyIndex < fNbPols; ++polyIndex) {
      UInt_t polySize = mesh->SizeOfPoly(polyIndex);

      fPolyDesc.push_back(polySize);

      for(i = 0; i < polySize; ++i) fPolyDesc.push_back(mesh->GetVertexIndex(polyIndex, i));
   }

   CalculateNormals();
}

//______________________________________________________________________________
void TGLFaceSet::DirectDraw(TGLRnrCtx & rnrCtx) const
{
   // Debug tracing
   if (gDebug > 4) {
      Info("TGLFaceSet::DirectDraw", "this %d (class %s) LOD %d", this, IsA()->GetName(), rnrCtx.ShapeLOD());
   }

   if (fNbPols == 0) return;

   GLUtesselator  *tessObj = TGLUtil::GetDrawTesselator3dv();
   const Double_t *pnts = &fVertices[0];
   const Double_t *normals = &fNormals[0];
   const Int_t *pols = &fPolyDesc[0];

   for (UInt_t i = 0, j = 0; i < fNbPols; ++i) {
      Int_t npoints = pols[j++];

      if (tessObj && npoints > 4) {
         gluBeginPolygon(tessObj);
         gluNextContour(tessObj, (GLenum)GLU_UNKNOWN);
         glNormal3dv(normals + i * 3);

         for (Int_t k = 0; k < npoints; ++k, ++j) {
            gluTessVertex(tessObj, (Double_t *)pnts + pols[j] * 3, (Double_t *)pnts + pols[j] * 3);
         }
         gluEndPolygon(tessObj);
      } else {
         glBegin(GL_POLYGON);
         glNormal3dv(normals + i * 3);

         for (Int_t k = 0; k < npoints; ++k, ++j) {
            glVertex3dv(pnts + pols[j] * 3);
         }
         glEnd();
      }
   }
}

//______________________________________________________________________________
Int_t TGLFaceSet::CheckPoints(const Int_t *source, Int_t *dest) const
{
   // CheckPoints
   const Double_t * p1 = &fVertices[source[0] * 3];
   const Double_t * p2 = &fVertices[source[1] * 3];
   const Double_t * p3 = &fVertices[source[2] * 3];
   Int_t retVal = 1;

   if (Eq(p1, p2)) {
      dest[0] = source[0];
      if (!Eq(p1, p3) ) {
         dest[1] = source[2];
         retVal = 2;
      }
   } else if (Eq(p1, p3)) {
      dest[0] = source[0];
      dest[1] = source[1];
      retVal = 2;
   } else {
      dest[0] = source[0];
      dest[1] = source[1];
      retVal = 2;
      if (!Eq(p2, p3)) {
         dest[2] = source[2];
         retVal = 3;
      }
   }

   return retVal;
}

//______________________________________________________________________________
Bool_t TGLFaceSet::Eq(const Double_t *p1, const Double_t *p2)
{
   // test equality
   Double_t dx = TMath::Abs(p1[0] - p2[0]);
   Double_t dy = TMath::Abs(p1[1] - p2[1]);
   Double_t dz = TMath::Abs(p1[2] - p2[2]);
   return dx < 1e-10 && dy < 1e-10 && dz < 1e-10;
}

//______________________________________________________________________________
void TGLFaceSet::CalculateNormals()
{
   // CalculateNormals

   if (fNbPols == 0) return;
   Double_t *pnts = &fVertices[0];
   for (UInt_t i = 0, j = 0; i < fNbPols; ++i) {
      Int_t polEnd = fPolyDesc[j] + j + 1;
      Int_t norm[] = {fPolyDesc[j + 1], fPolyDesc[j + 2], fPolyDesc[j + 3]};
      j += 4;
      Int_t check = CheckPoints(norm, norm), ngood = check;
      if (check == 3) {
         TMath::Normal2Plane(pnts + norm[0] * 3, pnts + norm[1] * 3,
                             pnts + norm[2] * 3, &fNormals[i * 3]);
         j = polEnd;
         continue;
      }
      while (j < (UInt_t)polEnd) {
         norm[ngood++] = fPolyDesc[j++];
         if (ngood == 3) {
            ngood = CheckPoints(norm, norm);
            if (ngood == 3) {
               TMath::Normal2Plane(pnts + norm[0] * 3, pnts + norm[1] * 3,
                                   pnts + norm[2] * 3, &fNormals[i * 3]);
               j = polEnd;
               break;
            }
         }
      }
   }
}
<|endoftext|>


<commit_before>/***************************************************************************
* Copyright (c) 2016, Johan Mabille and Sylvain Corlay                     *
*                                                                          *
* Distributed under the terms of the BSD 3-Clause License.                 *
*                                                                          *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/

#ifndef XTENSOR_JULIA_CONFIG_HPP
#define XTENSOR_JULIA_CONFIG_HPP

#define XTENSOR_JULIA_VERSION_MAJOR 0
#define XTENSOR_JULIA_VERSION_MINOR 0
#define XTENSOR_JULIA_VERSION_PATCH 6

#endif
<commit_msg>Release 0.0.7<commit_after>/***************************************************************************
* Copyright (c) 2016, Johan Mabille and Sylvain Corlay                     *
*                                                                          *
* Distributed under the terms of the BSD 3-Clause License.                 *
*                                                                          *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/

#ifndef XTENSOR_JULIA_CONFIG_HPP
#define XTENSOR_JULIA_CONFIG_HPP

#define XTENSOR_JULIA_VERSION_MAJOR 0
#define XTENSOR_JULIA_VERSION_MINOR 0
#define XTENSOR_JULIA_VERSION_PATCH 7

#endif
<|endoftext|>


<commit_before>/***************************************************************************/
/* Copyright (c) 2014 Linas Vepstas                                        */
/* All rights reserved                                                     */
/*                                                                         */
/* Use of the link grammar parsing system is subject to the terms of the   */
/* license set forth in the LICENSE file included with this software.      */
/* This license allows free redistribution and use in source and binary    */
/* forms, with or without modification, subject to certain conditions.     */
/*                                                                         */
/***************************************************************************/

// This implements a very simple-minded multi-threaded unit test.
// All it does is to make sure the system doesn't crash e.g. due to
// memory allocation conflicts.

#include <thread>
#include <vector>

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include "link-grammar/link-includes.h"

static void parse_one_sent(Dictionary dict, Parse_Options opts, const char *sent_str)
{
	Sentence sent = sentence_create(sent_str, dict);
	if (!sent) {
		fprintf (stderr, "Fatal error: Unable to create parser\n");
		exit(2);
	}
	sentence_split(sent, opts);
	int num_linkages = sentence_parse(sent, opts);
#if 0
	if (num_linkages <= 0) {
		fprintf (stderr, "Fatal error: Unable to parse sentence\n");
		exit(3);
	}
#endif
	if (0 < num_linkages)
	{
		if (10 < num_linkages) num_linkages = 10;

		for (int li = 0; li<num_linkages; li++)
		{
			Linkage linkage = linkage_create(li, sent, opts);

			// Short diagram, it wraps.
			char * str = linkage_print_diagram(linkage, true, 50);
			linkage_free_diagram(str);
			str = linkage_print_links_and_domains(linkage);
			linkage_free_links_and_domains(str);
			str = linkage_print_disjuncts(linkage);
			linkage_free_disjuncts(str);
			str = linkage_print_constituent_tree(linkage, SINGLE_LINE);
			linkage_free_constituent_tree_str(str);
			linkage_delete(linkage);
		}
	}
	sentence_delete(sent);
}

static void parse_sents(Dictionary dict, Parse_Options opts, int thread_id, int niter)
{
	const char *sents[] = {
		"Frank felt vindicated when his long time friend Bill revealed that he was the winner of the competition.",
		"Logorrhea, or excessive and often incoherent talkativeness or wordiness, is a social disease.",
		"It was covered with bites.",
		"I have no idea what that is.",
		"His shout had been involuntary, something anybody might have done.",
		"Trump, Ryan and McConnell are using the budget process to pay for the GOP’s $1.5 trillion tax scam.",
		"We ate popcorn and watched movies on TV for three days.",
		"Sweat stood on his brow, fury was bright in his one good eye.",
		"One of the things you do when you stop your bicycle is apply the brake.",
		"The line extends 10 miles offshore.",

		// The English parser will choke on this.
		"под броню боевого робота устремились потоки энергии.",
		"через четверть часа здесь будет полно полицейских.",

		// Rabindranath Tagore
		"習近平: 堅守 實體經濟落實高品 質發展",
		"文在寅希望半島對話氛 圍在平昌冬奧會後能延續",
		"土耳其出兵 搶先機 美土俄棋局怎麼走?",
		"默克爾努力獲突破 德社民黨同意開展組閣談判"
	};

	int nsents = sizeof(sents) / sizeof(const char *);

#define WID 120
#define LIN 4
	char junk[LIN*WID];
	for (int ln=0; ln<LIN; ln++)
	{
		char *line = &junk[ln*WID];
		for (int w = 0; w < WID; w++)
		{
			// Ugly junk including stuff that should be
			// bad/invalid UTF-8 character sequences.
			line[w] = (5*w+1)%30 + (31*ln ^ 0x66);
			if (30<w) line[w] += 0x7f;
			if (60<w) line[w] += 0x50;
			if (90<w) line[w] = line[w-90] ^ line[w-30];
			if (0 == w%25) line[w] = ' ';
			if (0 == w%23) line[w] = ' ';
			if (0 == w%15) line[w] = ' ';
			if (0 == w%11) line[w] = ' ';
		}
		line[WID-1] = 0x0;
	}

	for (int j=0; j<niter; j += nsents)
	{
		for (int i=0; i < nsents; ++i)
		{
			parse_one_sent(dict, opts, sents[i]);
		}
		for (int ln=0; ln<LIN; ln++)
		{
			char *line = &junk[ln*WID];
			parse_one_sent(dict, opts, line);
		}
	}
}

int main(int argc, char* argv[])
{
	setlocale(LC_ALL, "en_US.UTF-8");
	Parse_Options opts = parse_options_create();
	dictionary_set_data_dir(DICTIONARY_DIR "/data");
	// Dictionary dict = dictionary_create_lang("ru");
	Dictionary dict = dictionary_create_lang("en");
	if (!dict) {
		fprintf (stderr, "Fatal error: Unable to open the dictionary\n");
		exit(1);
	}

	int n_threads = 10;
	int niter = 100;

	printf("Creating %d threads, each parsing %d sentences\n",
		 n_threads, niter);
	std::vector<std::thread> thread_pool;
	for (int i=0; i < n_threads; i++) {
		thread_pool.push_back(std::thread(parse_sents, dict, opts, i, niter));
	}

	// Wait for all threads to complete
	for (std::thread& t : thread_pool) t.join();
	printf("Done with multi-threaded parsing\n");

	dictionary_delete(dict);
	parse_options_delete(opts);
	return 0;
}
<commit_msg>Disable spell-guessing on bad sentences<commit_after>/***************************************************************************/
/* Copyright (c) 2014 Linas Vepstas                                        */
/* All rights reserved                                                     */
/*                                                                         */
/* Use of the link grammar parsing system is subject to the terms of the   */
/* license set forth in the LICENSE file included with this software.      */
/* This license allows free redistribution and use in source and binary    */
/* forms, with or without modification, subject to certain conditions.     */
/*                                                                         */
/***************************************************************************/

// This implements a very simple-minded multi-threaded unit test.
// All it does is to make sure the system doesn't crash e.g. due to
// memory allocation conflicts.

#include <thread>
#include <vector>

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include "link-grammar/link-includes.h"

static void parse_one_sent(Dictionary dict, Parse_Options opts, const char *sent_str)
{
	Sentence sent = sentence_create(sent_str, dict);
	if (!sent) {
		fprintf (stderr, "Fatal error: Unable to create parser\n");
		exit(2);
	}
	sentence_split(sent, opts);
	int num_linkages = sentence_parse(sent, opts);
#if 0
	if (num_linkages <= 0) {
		fprintf (stderr, "Fatal error: Unable to parse sentence\n");
		exit(3);
	}
#endif
	if (0 < num_linkages)
	{
		if (10 < num_linkages) num_linkages = 10;

		for (int li = 0; li<num_linkages; li++)
		{
			Linkage linkage = linkage_create(li, sent, opts);

			// Short diagram, it wraps.
			char * str = linkage_print_diagram(linkage, true, 50);
			linkage_free_diagram(str);
			str = linkage_print_links_and_domains(linkage);
			linkage_free_links_and_domains(str);
			str = linkage_print_disjuncts(linkage);
			linkage_free_disjuncts(str);
			str = linkage_print_constituent_tree(linkage, SINGLE_LINE);
			linkage_free_constituent_tree_str(str);
			linkage_delete(linkage);
		}
	}
	sentence_delete(sent);
}

static void parse_sents(Dictionary dict, Parse_Options opts, int thread_id, int niter)
{
	const char *sents[] = {
		"Frank felt vindicated when his long time friend Bill revealed that he was the winner of the competition.",
		"Logorrhea, or excessive and often incoherent talkativeness or wordiness, is a social disease.",
		"It was covered with bites.",
		"I have no idea what that is.",
		"His shout had been involuntary, something anybody might have done.",
		"Trump, Ryan and McConnell are using the budget process to pay for the GOP’s $1.5 trillion tax scam.",
		"We ate popcorn and watched movies on TV for three days.",
		"Sweat stood on his brow, fury was bright in his one good eye.",
		"One of the things you do when you stop your bicycle is apply the brake.",
		"The line extends 10 miles offshore.",

		// The English parser will choke on this.
		"под броню боевого робота устремились потоки энергии.",
		"через четверть часа здесь будет полно полицейских.",

		// Rabindranath Tagore
		"習近平: 堅守 實體經濟落實高品 質發展",
		"文在寅希望半島對話氛 圍在平昌冬奧會後能延續",
		"土耳其出兵 搶先機 美土俄棋局怎麼走?",
		"默克爾努力獲突破 德社民黨同意開展組閣談判"
	};

	int nsents = sizeof(sents) / sizeof(const char *);

#define WID 120
#define LIN 4
	char junk[LIN*WID];
	for (int ln=0; ln<LIN; ln++)
	{
		char *line = &junk[ln*WID];
		for (int w = 0; w < WID; w++)
		{
			// Ugly junk including stuff that should be
			// bad/invalid UTF-8 character sequences.
			line[w] = ((thread_id+1)*w+1)%30 + (31*ln ^ 0x66);
			if (30<w) line[w] += 0x7f;
			if (60<w) line[w] += 0x50;
			if (90<w) line[w] = line[w-90] ^ line[w-30];
			if (0 == w%25) line[w] = ' ';
			if (0 == w%23) line[w] = ' ';
			if (0 == w%15) line[w] = ' ';
			if (0 == w%11) line[w] = ' ';
		}
		line[WID-1] = 0x0;
	}

	for (int j=0; j<niter; j += (nsents+LIN))
	{
		for (int i=0; i < nsents; ++i)
		{
			parse_one_sent(dict, opts, sents[i]);
		}
		for (int ln=0; ln<LIN; ln++)
		{
			char *line = &junk[ln*WID];
			parse_one_sent(dict, opts, line);
		}
	}
}

int main(int argc, char* argv[])
{
	setlocale(LC_ALL, "en_US.UTF-8");
	Parse_Options opts = parse_options_create();
	parse_options_set_spell_guess(opts, 0);

	dictionary_set_data_dir(DICTIONARY_DIR "/data");
	// Dictionary dict = dictionary_create_lang("ru");
	Dictionary dict = dictionary_create_lang("en");
	if (!dict) {
		fprintf (stderr, "Fatal error: Unable to open the dictionary\n");
		exit(1);
	}

	int n_threads = 10;
	int niter = 300;

	printf("Creating %d threads, each parsing %d sentences\n",
		 n_threads, niter);
	std::vector<std::thread> thread_pool;
	for (int i=0; i < n_threads; i++) {
		thread_pool.push_back(std::thread(parse_sents, dict, opts, i, niter));
	}

	// Wait for all threads to complete
	for (std::thread& t : thread_pool) t.join();
	printf("Done with multi-threaded parsing\n");

	dictionary_delete(dict);
	parse_options_delete(opts);
	return 0;
}
<|endoftext|>