<commit_before>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file main.cxx
 *
 * An application which acts as an openlcb hub with the GC protocol.
 *
 * @author Balazs Racz
 * @date 3 Aug 2013
 */

#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <unistd.h>

#include <memory>

#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/GridConnectHub.hxx"
#include "utils/GcTcpHub.hxx"
#include "utils/Crc.hxx"
#include "executor/Executor.hxx"
#include "executor/Service.hxx"

#include "nmranet/IfCan.hxx"
#include "nmranet/DatagramCan.hxx"
#include "nmranet/BootloaderClient.hxx"
#include "nmranet/If.hxx"
#include "nmranet/AliasAllocator.hxx"
#include "nmranet/DefaultNode.hxx"
#include "utils/socket_listener.hxx"

#include "freertos/bootloader_hal.h"

NO_THREAD nt;
Executor<1> g_executor(nt);
Service g_service(&g_executor);
CanHubFlow can_hub0(&g_service);

static const nmranet::NodeID NODE_ID = 0x05010101181FULL;

nmranet::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2);
nmranet::CanDatagramService g_datagram_can(&g_if_can, 10, 2);
static nmranet::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can);
nmranet::DefaultNode g_node(&g_if_can, NODE_ID);

namespace nmranet
{
Pool *const g_incoming_datagram_allocator = mainBufferPool;
}

int port = 12021;
const char *host = "localhost";
const char *device_path = nullptr;
const char *filename = nullptr;
uint64_t destination_nodeid = 0;
uint64_t destination_alias = 0;
int memory_space_id = 0xF0;
const char *checksum_algorithm = nullptr;

void usage(const char *e)
{
    fprintf(
        stderr,
        "Usage: %s ([-i destination_host] [-p port] | [-d device_path]) [-s "
        "memory_space_id] [-c csum_algo] (-n nodeid | -a "
        "alias) -f filename\n",
        e);
    fprintf(stderr, "Connects to an openlcb bus and performs the "
                    "bootloader protocol on openlcb node with id nodeid with "
                    "the contents of a given file.\n");
    fprintf(stderr,
            "The bus connection will be through an OpenLCB HUB on "
            "destination_host:port with OpenLCB over TCP "
            "(in GridConnect format) protocol, or through the CAN-USB device "
            "(also in GridConnect protocol) found at device_path. Device takes "
            "precedence over TCP host:port specification.");
    fprintf(stderr, "The default target is localhost:12021.\n");
    fprintf(stderr, "nodeid should be a 12-char hex string with 0x prefix and "
                    "no separators, like '-b 0x05010101141F'\n");
    fprintf(stderr, "alias should be a 3-char hex string with 0x prefix and no "
                    "separators, like '-a 0x3F9'\n");
    fprintf(stderr, "memory_space_if defines which memory space to write the "
                    "data into. Default is '-s 0xF0'.\n");
    fprintf(stderr, "csum_algo defines the checksum algorithm to use. If "
                    "omitted, no checksumming is done before writing the "
                    "data.\n");
    exit(1);
}

void parse_args(int argc, char *argv[])
{
    int opt;
    while ((opt = getopt(argc, argv, "hp:d:n:a:s:f:c:")) >= 0)
    {
        switch (opt)
        {
            case 'h':
                usage(argv[0]);
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'i':
                host = optarg;
                break;
            case 'd':
                device_path = optarg;
                break;
            case 'f':
                filename = optarg;
                break;
            case 'n':
                destination_nodeid = strtoll(optarg, nullptr, 16);
                break;
            case 'a':
                destination_alias = strtoul(optarg, nullptr, 16);
                break;
            case 's':
                memory_space_id = strtol(optarg, nullptr, 16);
                break;
            case 'c':
                checksum_algorithm = optarg;
                break;
            default:
                fprintf(stderr, "Unknown option %c\n", opt);
                usage(argv[0]);
        }
    }
    if (!filename || (!destination_nodeid && !destination_alias))
    {
        usage(argv[0]);
    }
}

nmranet::BootloaderClient bootloader_client(&g_node, &g_datagram_can,
                                            &g_if_can);
nmranet::BootloaderResponse response;

void maybe_checksum(string *firmware)
{
    if (!checksum_algorithm)
        return;
    string algo = checksum_algorithm;
    if (algo == "tiva123")
    {
        struct app_header hdr;
        memset(&hdr, 0, sizeof(hdr));
        // magic constant that comes from the size of the interrupt table. The
        // actual target has this in memory_map.ld.
        uint32_t offset = 0x270;
        if (firmware->size() < offset + sizeof(hdr))
        {
            fprintf(stderr, "Failed to checksum: firmware too small.\n");
            exit(1);
        }
        if (memcmp(&hdr, &(*firmware)[offset], sizeof(hdr)))
        {
            fprintf(stderr,
                    "Failed to checksum: location of checksum is not empty.\n");
            exit(1);
        }
        hdr.app_size = firmware->size();
        crc3_crc16_ibm(&(*firmware)[8], (offset - 8) & ~3,
                       (uint16_t *)hdr.checksum_pre);
        crc3_crc16_ibm(&(*firmware)[offset + sizeof(hdr)],
                       (firmware->size() - offset - sizeof(hdr)) & ~3,
                       (uint16_t *)hdr.checksum_post);
        memcpy(&(*firmware)[offset], &hdr, sizeof(hdr));
        printf("Checksummed firmware with algorithm tiva123\n");
    }
    else
    {
        fprintf(
            stderr,
            "Unknown checksumming algo %s. Known algorithms are: tiva123.\n",
            checksum_algorithm);
        exit(1);
    }
}

/** Entry point to application.
 * @param argc number of command line arguments
 * @param argv array of command line arguments
 * @return 0, should never return
 */
int appl_main(int argc, char *argv[])
{
    parse_args(argc, argv);
    int conn_fd = 0;
    if (device_path)
    {
        conn_fd = ::open(device_path, O_RDWR);
    }
    else
    {
        conn_fd = ConnectSocket("localhost", 12021);
    }
    HASSERT(conn_fd >= 0);
    create_gc_port_for_can_hub(&can_hub0, conn_fd);

    g_if_can.add_addressed_message_support();
    // Bootstraps the alias allocation process.
    g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc());

    g_executor.start_thread("g_executor", 0, 1024);
    usleep(400000);

    SyncNotifiable n;
    BarrierNotifiable bn(&n);
    Buffer<nmranet::BootloaderRequest> *b;
    mainBufferPool->alloc(&b);

    b->set_done(&bn);
    b->data()->dst.alias = destination_alias;
    b->data()->dst.id = destination_nodeid;
    b->data()->memory_space = memory_space_id;
    b->data()->offset = 0;
    b->data()->response = &response;

    FILE *f = fopen(filename, "rb");
    if (!f)
    {
        fprintf(stderr, "Could not open file %s: %s\n", filename,
                strerror(errno));
        exit(1);
    }
    char buf[1024];
    size_t nr;
    while ((nr = fread(buf, 1, sizeof(buf), f)) > 0)
    {
        b->data()->data.append(buf, nr);
    }
    fclose(f);
    printf("Read %d bytes from file %s. Writing to memory space 0x%02x\n",
           b->data()->data.size(), filename, memory_space_id);
    maybe_checksum(&b->data()->data);

    bootloader_client.send(b);
    n.wait_for_notification();
    printf("Result: %04x  %s\n", response.error_code,
           response.error_details.c_str());

    return 0;
}
<commit_msg>Adds a flag to request target rebooting into bootloader mode.<commit_after>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file main.cxx
 *
 * An application which acts as an openlcb hub with the GC protocol.
 *
 * @author Balazs Racz
 * @date 3 Aug 2013
 */

#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <unistd.h>

#include <memory>

#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/GridConnectHub.hxx"
#include "utils/GcTcpHub.hxx"
#include "utils/Crc.hxx"
#include "executor/Executor.hxx"
#include "executor/Service.hxx"

#include "nmranet/IfCan.hxx"
#include "nmranet/DatagramCan.hxx"
#include "nmranet/BootloaderClient.hxx"
#include "nmranet/If.hxx"
#include "nmranet/AliasAllocator.hxx"
#include "nmranet/DefaultNode.hxx"
#include "utils/socket_listener.hxx"

#include "freertos/bootloader_hal.h"

NO_THREAD nt;
Executor<1> g_executor(nt);
Service g_service(&g_executor);
CanHubFlow can_hub0(&g_service);

static const nmranet::NodeID NODE_ID = 0x05010101181FULL;

nmranet::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2);
nmranet::CanDatagramService g_datagram_can(&g_if_can, 10, 2);
static nmranet::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can);
nmranet::DefaultNode g_node(&g_if_can, NODE_ID);

namespace nmranet
{
Pool *const g_incoming_datagram_allocator = mainBufferPool;
}

int port = 12021;
const char *host = "localhost";
const char *device_path = nullptr;
const char *filename = nullptr;
uint64_t destination_nodeid = 0;
uint64_t destination_alias = 0;
int memory_space_id = 0xF0;
const char *checksum_algorithm = nullptr;
bool request_reboot = false;

void usage(const char *e)
{
    fprintf(stderr,
        "Usage: %s ([-i destination_host] [-p port] | [-d device_path]) [-s "
        "memory_space_id] [-c csum_algo] [-r] (-n nodeid | -a "
        "alias) -f filename\n",
        e);
    fprintf(stderr, "Connects to an openlcb bus and performs the "
                    "bootloader protocol on openlcb node with id nodeid with "
                    "the contents of a given file.\n");
    fprintf(stderr,
        "The bus connection will be through an OpenLCB HUB on "
        "destination_host:port with OpenLCB over TCP "
        "(in GridConnect format) protocol, or through the CAN-USB device "
        "(also in GridConnect protocol) found at device_path. Device takes "
        "precedence over TCP host:port specification.");
    fprintf(stderr, "The default target is localhost:12021.\n");
    fprintf(stderr, "nodeid should be a 12-char hex string with 0x prefix and "
                    "no separators, like '-b 0x05010101141F'\n");
    fprintf(stderr, "alias should be a 3-char hex string with 0x prefix and no "
                    "separators, like '-a 0x3F9'\n");
    fprintf(stderr, "memory_space_if defines which memory space to write the "
                    "data into. Default is '-s 0xF0'.\n");
    fprintf(stderr, "csum_algo defines the checksum algorithm to use. If "
                    "omitted, no checksumming is done before writing the "
                    "data.\n");
    fprintf(stderr,
        "-r request the target to enter bootloader mode before sending data\n");
    exit(1);
}

void parse_args(int argc, char *argv[])
{
    int opt;
    while ((opt = getopt(argc, argv, "hp:rd:n:a:s:f:c:")) >= 0)
    {
        switch (opt)
        {
            case 'h':
                usage(argv[0]);
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'i':
                host = optarg;
                break;
            case 'd':
                device_path = optarg;
                break;
            case 'f':
                filename = optarg;
                break;
            case 'n':
                destination_nodeid = strtoll(optarg, nullptr, 16);
                break;
            case 'a':
                destination_alias = strtoul(optarg, nullptr, 16);
                break;
            case 's':
                memory_space_id = strtol(optarg, nullptr, 16);
                break;
            case 'c':
                checksum_algorithm = optarg;
                break;
            case 'r':
                request_reboot = true;
                break;
            default:
                fprintf(stderr, "Unknown option %c\n", opt);
                usage(argv[0]);
        }
    }
    if (!filename || (!destination_nodeid && !destination_alias))
    {
        usage(argv[0]);
    }
}

nmranet::BootloaderClient bootloader_client(
    &g_node, &g_datagram_can, &g_if_can);
nmranet::BootloaderResponse response;

void maybe_checksum(string *firmware)
{
    if (!checksum_algorithm)
        return;
    string algo = checksum_algorithm;
    if (algo == "tiva123")
    {
        struct app_header hdr;
        memset(&hdr, 0, sizeof(hdr));
        // magic constant that comes from the size of the interrupt table. The
        // actual target has this in memory_map.ld.
        uint32_t offset = 0x270;
        if (firmware->size() < offset + sizeof(hdr))
        {
            fprintf(stderr, "Failed to checksum: firmware too small.\n");
            exit(1);
        }
        if (memcmp(&hdr, &(*firmware)[offset], sizeof(hdr)))
        {
            fprintf(stderr,
                "Failed to checksum: location of checksum is not empty.\n");
            exit(1);
        }
        hdr.app_size = firmware->size();
        crc3_crc16_ibm(
            &(*firmware)[8], (offset - 8) & ~3, (uint16_t *)hdr.checksum_pre);
        crc3_crc16_ibm(&(*firmware)[offset + sizeof(hdr)],
            (firmware->size() - offset - sizeof(hdr)) & ~3,
            (uint16_t *)hdr.checksum_post);
        memcpy(&(*firmware)[offset], &hdr, sizeof(hdr));
        printf("Checksummed firmware with algorithm tiva123\n");
    }
    else
    {
        fprintf(stderr,
            "Unknown checksumming algo %s. Known algorithms are: tiva123.\n",
            checksum_algorithm);
        exit(1);
    }
}

/** Entry point to application.
 * @param argc number of command line arguments
 * @param argv array of command line arguments
 * @return 0, should never return
 */
int appl_main(int argc, char *argv[])
{
    parse_args(argc, argv);
    int conn_fd = 0;
    if (device_path)
    {
        conn_fd = ::open(device_path, O_RDWR);
    }
    else
    {
        conn_fd = ConnectSocket("localhost", 12021);
    }
    HASSERT(conn_fd >= 0);
    create_gc_port_for_can_hub(&can_hub0, conn_fd);

    g_if_can.add_addressed_message_support();
    // Bootstraps the alias allocation process.
    g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc());

    g_executor.start_thread("g_executor", 0, 1024);
    usleep(400000);

    SyncNotifiable n;
    BarrierNotifiable bn(&n);
    Buffer<nmranet::BootloaderRequest> *b;
    mainBufferPool->alloc(&b);

    b->set_done(&bn);
    b->data()->dst.alias = destination_alias;
    b->data()->dst.id = destination_nodeid;
    b->data()->memory_space = memory_space_id;
    b->data()->offset = 0;
    b->data()->response = &response;
    b->data()->request_reboot = request_reboot ? 1 : 0;

    FILE *f = fopen(filename, "rb");
    if (!f)
    {
        fprintf(
            stderr, "Could not open file %s: %s\n", filename, strerror(errno));
        exit(1);
    }
    char buf[1024];
    size_t nr;
    while ((nr = fread(buf, 1, sizeof(buf), f)) > 0)
    {
        b->data()->data.append(buf, nr);
    }
    fclose(f);
    printf("Read %d bytes from file %s. Writing to memory space 0x%02x\n",
        b->data()->data.size(), filename, memory_space_id);
    maybe_checksum(&b->data()->data);

    bootloader_client.send(b);
    n.wait_for_notification();
    printf("Result: %04x  %s\n", response.error_code,
        response.error_details.c_str());

    return 0;
}
<|endoftext|>


<commit_before>#include <stdint.h>
#include <iostream>
#include <vector>
#include <cmath>
#include <Collective.hpp>
#include <ParallelLoop.hpp>
#include <Delegate.hpp>
#include <Grappa.hpp>
#include "local_graph.hpp"
#include "Hypercube.hpp"

// graph includes
#include "../graph500/grappa/graph.hpp"
#include "../graph500/generator/make_graph.h"
#include "../graph500/generator/utils.h"
#include "../graph500/prng.h"

#include "../graph500/grappa/graph.hpp"

using namespace Grappa;

#define DIFFERENT_RELATIONS 0
#define DEDUP_EDGES 1

// Currently assumes an undirected graph implemented as a large directed graph!!

DEFINE_uint64( scale, 7, "Graph will have ~ 2^scale vertices" );
DEFINE_uint64( edgefactor, 16, "Median degree; graph will have ~ 2*edgefactor*2^scale edges" );
DEFINE_uint64( progressInterval, 5, "interval between progress updates" );

GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, edges_transfered, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, total_edges, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, participating_cores, 0);

// intermediate results counts
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir1_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir2_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir3_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir4_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir5_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir6_count, 0);

//outputs
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, results_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<double>, query_runtime, 0);

double generation_time;
double construction_time;

uint64_t count = 0;
uint64_t edgesSent = 0;

void emit(int64_t x, int64_t y, int64_t z, int64_t t) {
//  std::cout << x << " " << y << " " << z << std::endl;
  count++;
}

std::vector<Edge> localAssignedEdges_R1;
std::vector<Edge> localAssignedEdges_R2;
std::vector<Edge> localAssignedEdges_R3;
std::vector<Edge> localAssignedEdges_R4;

//TODO
std::function<int64_t (int64_t)> makeHash( int64_t dim ) {
  // identity
  return [dim](int64_t x) { return x % dim; };
}

int64_t fourth_root(int64_t x) {
  // index pow 4
  std::vector<int64_t> powers = {0, 1, 16, 81, 256, 625, 1296, 2401};
  int64_t ind = powers.size() / 2;
  int64_t hi = powers.size()-1;
  int64_t lo = 0;
  while(true) {
    if (x == powers[ind]) {
      return ind;
    } else if (x > powers[ind]) {
      int64_t next = (ind+hi)/2;
      if (next - ind == 0) {
        return ind;
      }
      lo = ind;
      ind = next;
    } else {
      int64_t next = (ind+lo)/2;
      hi = ind;
      ind = next;
    }
  }
}

void squares(GlobalAddress<Graph<Vertex>> g) {

  on_all_cores( [] { Grappa::Statistics::reset(); } );

  // arrange the processors in 4d
  auto sidelength = fourth_root(cores());
  LOG(INFO) << "using " << sidelength*sidelength*sidelength*sidelength << " of " << cores() << " cores";
  participating_cores = sidelength*sidelength*sidelength*sidelength;

  double start, end;
  start = Grappa_walltime();

  // 1. Send edges to the partitions
  //
  // really just care about local edges; we get to them
  // indirectly through local vertices at the moment.
  // This is sequential access since edgeslists and vertices are sorted the same
  forall_localized( g->vs, g->nv, [sidelength](int64_t i, Vertex& v) {
    // hash function
    auto hf = makeHash( sidelength );
    Hypercube h( { sidelength, sidelength, sidelength, sidelength } );

    for (auto& dest : v.adj_iter()) {

      total_edges += 1;

      const int64_t src = i;
      const int64_t dst = dest;

      // a->b
      auto locs_ab = h.slice( {hf(src), hf(dst), HypercubeSlice::ALL, HypercubeSlice::ALL} );
      for (auto l : locs_ab) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R1.push_back(e);
        });
        edgesSent++;
      }

      // b->c
      auto locs_bc = h.slice( {HypercubeSlice::ALL, hf(src), hf(dst), HypercubeSlice::ALL} );
      for (auto l : locs_bc) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R2.push_back(e);
        });
        edgesSent++;
      }

      // c->d
      auto locs_cd = h.slice( {HypercubeSlice::ALL, HypercubeSlice::ALL, hf(src), hf(dst)} );
      for (auto l : locs_cd) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R3.push_back(e);
        });
        edgesSent++;
      }

      // d->a
      auto locs_da = h.slice( {hf(dst), HypercubeSlice::ALL, HypercubeSlice::ALL, hf(src)} );
      for (auto l : locs_da) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R4.push_back(e);
        });
        edgesSent++;
      }
    }
  });
  on_all_cores([] {
      LOG(INFO) << "edges sent: " << edgesSent;
      edges_transfered += edgesSent;
  });

  // 2. compute squares locally
  //
  on_all_cores([] {

    LOG(INFO) << "received (" << localAssignedEdges_R1.size() << ", " << localAssignedEdges_R2.size() << ", " << localAssignedEdges_R3.size() << ", " <<  localAssignedEdges_R4.size() << ") edges";

#ifdef DEDUP_EDGES
    // construct local graphs
    LOG(INFO) << "dedup...";
    std::unordered_set<Edge, Edge_hasher> R1_dedup;
    std::unordered_set<Edge, Edge_hasher> R2_dedup;
    std::unordered_set<Edge, Edge_hasher> R3_dedup;
    std::unordered_set<Edge, Edge_hasher> R4_dedup;
    for (auto e : localAssignedEdges_R1) { R1_dedup.insert( e ); }
    for (auto e : localAssignedEdges_R2) { R2_dedup.insert( e ); }
    for (auto e : localAssignedEdges_R3) { R3_dedup.insert( e ); }
    for (auto e : localAssignedEdges_R3) { R4_dedup.insert( e ); }

    LOG(INFO) << "after dedup (" << R1_dedup.size() << ", " << R2_dedup.size() << ", " << R3_dedup.size() << ", " << R4_dedup.size() << ") edges";

    localAssignedEdges_R1.resize(1);
    localAssignedEdges_R2.resize(1);
    localAssignedEdges_R3.resize(1);
    localAssignedEdges_R4.resize(1);

    LOG(INFO) << "local graph construct...";
    auto& R1 = *new LocalAdjListGraph(R1_dedup);
    auto& R2 = *new LocalAdjListGraph(R2_dedup);
    auto& R3 = *new LocalAdjListGraph(R3_dedup);
    auto& R4 = *new LocalMapGraph(R4_dedup);
#else
    LOG(INFO) << "local graph construct...";
    auto& R1 = *new LocalAdjListGraph(localAssignedEdges_R1);
    auto& R2 = *new LocalAdjListGraph(localAssignedEdges_R2);
    auto& R3 = *new LocalAdjListGraph(localAssignedEdges_R3);
    auto& R4 = *new LocalMapGraph(localAssignedEdges_R4);
#endif

    // use number of adjacencies (degree) as the ordering
               // EVAR: implementation of triangle count
    int64_t i=0;
    for (auto xy : R1.vertices()) {
      ir1_count++;
      int64_t x = xy.first;
      auto& xadj = xy.second;
      for (auto y : xadj) {
        ir2_count++;
        auto& yadj = R2.neighbors(y);
        if (xadj.size() < yadj.size()) {
          ir3_count++;
          for (auto z : yadj) {
            ir4_count++;
            auto& zadj = R3.neighbors(y);
            if (yadj.size() < zadj.size()) {
              ir5_count++;
              for (auto t : zadj) {
                ir6_count++;
                if (R4.inNeighborhood(t, x)) {
                  emit( x,y,z,t );
                  results_count++;
                } // end select t.dst=x
              } // end over t
            } // end select y < z
          } // end over z
        } // end select x < y
      } // end over y
    } // end over x

    LOG(INFO) << "counted " << count << " squares";
  });
  end = Grappa_walltime();
  query_runtime = end - start;

  Grappa::Statistics::merge_and_print();

}

void user_main( int * ignore ) {
  double t, start_time;
  start_time = walltime();

	int64_t nvtx_scale = ((int64_t)1)<<FLAGS_scale;
	int64_t desired_nedge = nvtx_scale * FLAGS_edgefactor;

  LOG(INFO) << "scale = " << FLAGS_scale << ", NV = " << nvtx_scale << ", NE = " << desired_nedge;

  // make raw graph edge tuples
  tuple_graph tg;
  tg.edges = global_alloc<packed_edge>(desired_nedge);

  LOG(INFO) << "graph generation...";
  t = walltime();
  make_graph( FLAGS_scale, desired_nedge, userseed, userseed, &tg.nedge, &tg.edges );
  generation_time = walltime() - t;
  LOG(INFO) << "graph_generation: " << generation_time;

  LOG(INFO) << "graph construction...";
  t = walltime();
  auto g = Graph<Vertex>::create(tg);
  construction_time = walltime() - t;
  LOG(INFO) << "construction_time: " << construction_time;

  LOG(INFO) << "num edges: " << g->nadj * 3; /* 3 b/c three copies*/

  LOG(INFO) << "query start...";
  squares(g);
}

int main(int argc, char** argv) {
  Grappa_init(&argc, &argv);
  Grappa_activate();

  Grappa_run_user_main(&user_main, (int*)NULL);

  Grappa_finish(0);
  return 0;
}
<commit_msg>track max/min for intermediate results<commit_after>#include <stdint.h>
#include <iostream>
#include <vector>
#include <cmath>
#include <Collective.hpp>
#include <ParallelLoop.hpp>
#include <Delegate.hpp>
#include <Grappa.hpp>
#include "local_graph.hpp"
#include "Hypercube.hpp"

// graph includes
#include "../graph500/grappa/graph.hpp"
#include "../graph500/generator/make_graph.h"
#include "../graph500/generator/utils.h"
#include "../graph500/prng.h"

#include "../graph500/grappa/graph.hpp"

using namespace Grappa;

#define DIFFERENT_RELATIONS 0
#define DEDUP_EDGES 1

// Currently assumes an undirected graph implemented as a large directed graph!!

DEFINE_uint64( scale, 7, "Graph will have ~ 2^scale vertices" );
DEFINE_uint64( edgefactor, 16, "Median degree; graph will have ~ 2*edgefactor*2^scale edges" );
DEFINE_uint64( progressInterval, 5, "interval between progress updates" );

GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, edges_transfered, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, total_edges, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, participating_cores, 0);

// intermediate results counts
// only care about min, max, totals
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir1_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir2_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir3_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir4_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir5_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, ir6_count, 0);

GRAPPA_DEFINE_STAT(SummarizingStatistic<uint64_t>, ir1_final_count, 0);
GRAPPA_DEFINE_STAT(SummarizingStatistic<uint64_t>, ir2_final_count, 0);
GRAPPA_DEFINE_STAT(SummarizingStatistic<uint64_t>, ir3_final_count, 0);
GRAPPA_DEFINE_STAT(SummarizingStatistic<uint64_t>, ir4_final_count, 0);
GRAPPA_DEFINE_STAT(SummarizingStatistic<uint64_t>, ir5_final_count, 0);
GRAPPA_DEFINE_STAT(SummarizingStatistic<uint64_t>, ir6_final_count, 0);

//outputs
GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, results_count, 0);
GRAPPA_DEFINE_STAT(SimpleStatistic<double>, query_runtime, 0);

double generation_time;
double construction_time;

uint64_t count = 0;
uint64_t edgesSent = 0;

void emit(int64_t x, int64_t y, int64_t z, int64_t t) {
//  std::cout << x << " " << y << " " << z << std::endl;
  count++;
}

std::vector<Edge> localAssignedEdges_R1;
std::vector<Edge> localAssignedEdges_R2;
std::vector<Edge> localAssignedEdges_R3;
std::vector<Edge> localAssignedEdges_R4;

//TODO
std::function<int64_t (int64_t)> makeHash( int64_t dim ) {
  // identity
  return [dim](int64_t x) { return x % dim; };
}

int64_t fourth_root(int64_t x) {
  // index pow 4
  std::vector<int64_t> powers = {0, 1, 16, 81, 256, 625, 1296, 2401};
  int64_t ind = powers.size() / 2;
  int64_t hi = powers.size()-1;
  int64_t lo = 0;
  while(true) {
    if (x == powers[ind]) {
      return ind;
    } else if (x > powers[ind]) {
      int64_t next = (ind+hi)/2;
      if (next - ind == 0) {
        return ind;
      }
      lo = ind;
      ind = next;
    } else {
      int64_t next = (ind+lo)/2;
      hi = ind;
      ind = next;
    }
  }
}

//int64_t random_assignment(int64_t x) {
//  std::random_device rd;
//
//  // Choose a random mean between 1 and 6
//  std::default_random_engine e1(rd());
//  const int64_t root1 = fourth_root(x);
//  std::normal_distribution<> normal_dist1(root1, root1/1.25);
//
//  const int64_t share1 = std::round(max(1, normal_dist1(e1)));
//  const int64_t left1 = x/share1;
//  const int64_t root2 = std::round(cbrt(left));
//  std::normal_distribution<> normal_dist2(root2, root2/1.25);
//  const int64_t share2 = std::round(max(1, normal_dist2(e1)));
//  const int64_t left2 =  left1/share2;
//

void squares(GlobalAddress<Graph<Vertex>> g) {

  on_all_cores( [] { Grappa::Statistics::reset(); } );

  // arrange the processors in 4d
  auto sidelength = fourth_root(cores());
  LOG(INFO) << "using " << sidelength*sidelength*sidelength*sidelength << " of " << cores() << " cores";
  participating_cores = sidelength*sidelength*sidelength*sidelength;

  double start, end;
  start = Grappa_walltime();

  // 1. Send edges to the partitions
  //
  // really just care about local edges; we get to them
  // indirectly through local vertices at the moment.
  // This is sequential access since edgeslists and vertices are sorted the same
  forall_localized( g->vs, g->nv, [sidelength](int64_t i, Vertex& v) {
    // hash function
    auto hf = makeHash( sidelength );
    Hypercube h( { sidelength, sidelength, sidelength, sidelength } );

    for (auto& dest : v.adj_iter()) {

      total_edges += 4; // count 4 for 4 relations

      const int64_t src = i;
      const int64_t dst = dest;

      // a->b
      auto locs_ab = h.slice( {hf(src), hf(dst), HypercubeSlice::ALL, HypercubeSlice::ALL} );
      for (auto l : locs_ab) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R1.push_back(e);
        });
        edgesSent++;
      }

      // b->c
      auto locs_bc = h.slice( {HypercubeSlice::ALL, hf(src), hf(dst), HypercubeSlice::ALL} );
      for (auto l : locs_bc) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R2.push_back(e);
        });
        edgesSent++;
      }

      // c->d
      auto locs_cd = h.slice( {HypercubeSlice::ALL, HypercubeSlice::ALL, hf(src), hf(dst)} );
      for (auto l : locs_cd) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R3.push_back(e);
        });
        edgesSent++;
      }

      // d->a
      auto locs_da = h.slice( {hf(dst), HypercubeSlice::ALL, HypercubeSlice::ALL, hf(src)} );
      for (auto l : locs_da) {
        Edge e(src, dst);
        delegate::call_async( l, [e] {
          localAssignedEdges_R4.push_back(e);
        });
        edgesSent++;
      }
    }
  });
  on_all_cores([] {
      LOG(INFO) << "edges sent: " << edgesSent;
      edges_transfered += edgesSent;
  });

  // 2. compute squares locally
  //
  on_all_cores([] {

    LOG(INFO) << "received (" << localAssignedEdges_R1.size() << ", " << localAssignedEdges_R2.size() << ", " << localAssignedEdges_R3.size() << ", " <<  localAssignedEdges_R4.size() << ") edges";

#ifdef DEDUP_EDGES
    // construct local graphs
    LOG(INFO) << "dedup...";
    std::unordered_set<Edge, Edge_hasher> R1_dedup;
    std::unordered_set<Edge, Edge_hasher> R2_dedup;
    std::unordered_set<Edge, Edge_hasher> R3_dedup;
    std::unordered_set<Edge, Edge_hasher> R4_dedup;
    for (auto e : localAssignedEdges_R1) { R1_dedup.insert( e ); }
    for (auto e : localAssignedEdges_R2) { R2_dedup.insert( e ); }
    for (auto e : localAssignedEdges_R3) { R3_dedup.insert( e ); }
    for (auto e : localAssignedEdges_R3) { R4_dedup.insert( e ); }

    LOG(INFO) << "after dedup (" << R1_dedup.size() << ", " << R2_dedup.size() << ", " << R3_dedup.size() << ", " << R4_dedup.size() << ") edges";

    localAssignedEdges_R1.resize(1);
    localAssignedEdges_R2.resize(1);
    localAssignedEdges_R3.resize(1);
    localAssignedEdges_R4.resize(1);

    LOG(INFO) << "local graph construct...";
    auto& R1 = *new LocalAdjListGraph(R1_dedup);
    auto& R2 = *new LocalAdjListGraph(R2_dedup);
    auto& R3 = *new LocalAdjListGraph(R3_dedup);
    auto& R4 = *new LocalMapGraph(R4_dedup);
#else
    LOG(INFO) << "local graph construct...";
    auto& R1 = *new LocalAdjListGraph(localAssignedEdges_R1);
    auto& R2 = *new LocalAdjListGraph(localAssignedEdges_R2);
    auto& R3 = *new LocalAdjListGraph(localAssignedEdges_R3);
    auto& R4 = *new LocalMapGraph(localAssignedEdges_R4);
#endif

    // use number of adjacencies (degree) as the ordering
               // EVAR: implementation of triangle count
    int64_t i=0;
    for (auto xy : R1.vertices()) {
      ir1_count++;
      int64_t x = xy.first;
      auto& xadj = xy.second;
      for (auto y : xadj) {
        ir2_count++;
        auto& yadj = R2.neighbors(y);
        if (xadj.size() < yadj.size()) {
          ir3_count++;
          for (auto z : yadj) {
            ir4_count++;
            auto& zadj = R3.neighbors(y);
            if (yadj.size() < zadj.size()) {
              ir5_count++;
              for (auto t : zadj) {
                ir6_count++;
                if (R4.inNeighborhood(t, x)) {
                  emit( x,y,z,t );
                  results_count++;
                } // end select t.dst=x
              } // end over t
            } // end select y < z
          } // end over z
        } // end select x < y
      } // end over y
    } // end over x

    LOG(INFO) << "counted " << count << " squares";

    // used to calculate max total over all cores
    ir1_final_count=ir1_count;
    ir2_final_count=ir2_count;
    ir3_final_count=ir3_count;
    ir4_final_count=ir4_count;
    ir5_final_count=ir5_count;
    ir6_final_count=ir6_count;

  });
  end = Grappa_walltime();
  query_runtime = end - start;

  Grappa::Statistics::merge_and_print();

}

void user_main( int * ignore ) {
  double t, start_time;
  start_time = walltime();

	int64_t nvtx_scale = ((int64_t)1)<<FLAGS_scale;
	int64_t desired_nedge = nvtx_scale * FLAGS_edgefactor;

  LOG(INFO) << "scale = " << FLAGS_scale << ", NV = " << nvtx_scale << ", NE = " << desired_nedge;

  // make raw graph edge tuples
  tuple_graph tg;
  tg.edges = global_alloc<packed_edge>(desired_nedge);

  LOG(INFO) << "graph generation...";
  t = walltime();
  make_graph( FLAGS_scale, desired_nedge, userseed, userseed, &tg.nedge, &tg.edges );
  generation_time = walltime() - t;
  LOG(INFO) << "graph_generation: " << generation_time;

  LOG(INFO) << "graph construction...";
  t = walltime();
  auto g = Graph<Vertex>::create(tg);
  construction_time = walltime() - t;
  LOG(INFO) << "construction_time: " << construction_time;

  LOG(INFO) << "num edges: " << g->nadj * 3; /* 3 b/c three copies*/

  LOG(INFO) << "query start...";
  squares(g);
}

int main(int argc, char** argv) {
  Grappa_init(&argc, &argv);
  Grappa_activate();

  Grappa_run_user_main(&user_main, (int*)NULL);

  Grappa_finish(0);
  return 0;
}
<|endoftext|>


<commit_before>#include "Halide.h"

namespace {

class MatMul : public Halide::Generator<MatMul> {
public:

    GeneratorParam<int>   size {"size", 1024};
    ImageParam            A {Float(32), 2, "A"};
    ImageParam            B {Float(32), 2, "B"};

    Func build() {
        Var x, y;

        Func prod("prod");
        RDom r(0, size);
        prod(x, y) += A(x, r) * B(r, y);

        Var xi, yi, xii, yii;
        Func out = prod.in();
        out.bound(x, 0, size)
            .bound(y, 0, size)
            .tile(x, y, xi, yi, 16, 8)
            .vectorize(xi, 4)
            .unroll(xi)
            .unroll(yi)
            .gpu_tile(x, y, 8, 8);
        prod.compute_at(out, Var::gpu_threads())
            .unroll(x)
            .unroll(y)
            .update()
            .reorder(x, y, r.x)
            .tile(x, y, xi, yi, 2, 2)
            .vectorize(xi)
            .unroll(yi)
            .tile(x, y, xii, yii, 2, 2)
            .unroll(xii)
            .unroll(yii)
            .unroll(x)
            .unroll(y);

        Halide::OutputImageParam bufs[] = {A, B, out.output_buffer()};
        for (auto &buf : bufs) {
            buf.set_host_alignment(16)
                .set_bounds(0, 0, size)
                .set_stride(1, size);
        }

        return out;
    }
};

Halide::RegisterGenerator<MatMul> register_me{"mat_mul"};
}
<commit_msg>Slightly better schedule<commit_after>#include "Halide.h"

using namespace Halide;

namespace {

class MatMul : public Halide::Generator<MatMul> {
public:

    GeneratorParam<int>   size {"size", 1024};
    ImageParam            A {Float(32), 2, "A"};
    ImageParam            B {Float(32), 2, "B"};

    Func build() {
        Var x("x"), y("y");

        Func prod("prod");
        RDom r(0, size);
        prod(x, y) += A(x, r) * B(r, y);

        Var xi, yi, xio, xii, yii;
        Func out = prod.in();
        out.bound(x, 0, size)
            .bound(y, 0, size)
            .tile(x, y, xi, yi, 8*32, 8)
            .split(xi, xio, xii, 32)
            .reorder(xio, yi, xii, x, y)
            .unroll(xio)
            .unroll(yi)
            .gpu_blocks(x, y).gpu_threads(xii);
        prod.compute_at(out, Var::gpu_threads())
            .unroll(x)
            .unroll(y)
            .update()
            .reorder(x, y, r.x)
            .unroll(x)
            .unroll(y);
        OutputImageParam bufs[] = {A, B, out.output_buffer()};
        for (auto &buf : bufs) {
            buf.set_host_alignment(16)
                .set_bounds(0, 0, size)
                .set_stride(1, size);
        }

        return out;
    }
};

Halide::RegisterGenerator<MatMul> register_me{"mat_mul"};
}
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4      *
*                (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library 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 library 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 library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <sofa/simulation/common/ResetVisitor.h>
#include <sofa/helper/Factory.h>
#include <sofa/simulation/common/Node.h>

namespace sofa
{

namespace simulation
{

void ResetVisitor::processObject(core::objectmodel::BaseObject* obj)
{
    obj->reset();
    obj->clearWarnings(); obj->clearOutputs();
}

Visitor::Result ResetVisitor::processNodeTopDown(simulation::Node* node)
{
    for (simulation::Node::ObjectIterator it = node->object.begin(); it != node->object.end(); ++it)
    {
        this->processObject(*it);
    }

    {
        if (!node->nodeInVisualGraph.empty()) node->nodeInVisualGraph->execute<ResetVisitor>(this->params);
        for (simulation::Node::ChildIterator itChild = node->childInVisualGraph.begin(); itChild != node->childInVisualGraph.end(); ++itChild)
        {
            simulation::Node *child=*itChild;
            child->execute<ResetVisitor>(this->params);
        }
    }

    node->clearWarnings(); node->clearOutputs();
    return RESULT_CONTINUE;
}

void ResetVisitor::processNodeBottomUp(simulation::Node* /*node*/)
{
}

void StoreResetStateVisitor::processObject(core::objectmodel::BaseObject* obj)
{
    obj->storeResetState();
}

Visitor::Result StoreResetStateVisitor::processNodeTopDown(simulation::Node* node)
{
    for (simulation::Node::ObjectIterator it = node->object.begin(); it != node->object.end(); ++it)
    {
        this->processObject(*it);
    }

    {
        if (!node->nodeInVisualGraph.empty()) node->nodeInVisualGraph->execute<StoreResetStateVisitor>(this->params);
        for (simulation::Node::ChildIterator itChild = node->childInVisualGraph.begin(); itChild != node->childInVisualGraph.end(); ++itChild)
        {
            simulation::Node *child=*itChild;
            child->execute<StoreResetStateVisitor>(this->params);
        }
    }
    return RESULT_CONTINUE;
}

void StoreResetStateVisitor::processNodeBottomUp(simulation::Node* /*node*/)
{
}

} // namespace simulation

} // namespace sofa

<commit_msg>r10189/sofa-dev : CHANGE: ResetVisitor no longer clear messages in all components, allowing to see warnings generated during load and init (as reset is called right after init)<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4      *
*                (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library 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 library 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 library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <sofa/simulation/common/ResetVisitor.h>
#include <sofa/helper/Factory.h>
#include <sofa/simulation/common/Node.h>

namespace sofa
{

namespace simulation
{

void ResetVisitor::processObject(core::objectmodel::BaseObject* obj)
{
    obj->reset();
    //obj->clearWarnings(); obj->clearOutputs();
}

Visitor::Result ResetVisitor::processNodeTopDown(simulation::Node* node)
{
    for (simulation::Node::ObjectIterator it = node->object.begin(); it != node->object.end(); ++it)
    {
        this->processObject(*it);
    }

    {
        if (!node->nodeInVisualGraph.empty()) node->nodeInVisualGraph->execute<ResetVisitor>(this->params);
        for (simulation::Node::ChildIterator itChild = node->childInVisualGraph.begin(); itChild != node->childInVisualGraph.end(); ++itChild)
        {
            simulation::Node *child=*itChild;
            child->execute<ResetVisitor>(this->params);
        }
    }

    node->clearWarnings(); node->clearOutputs();
    return RESULT_CONTINUE;
}

void ResetVisitor::processNodeBottomUp(simulation::Node* /*node*/)
{
}

void StoreResetStateVisitor::processObject(core::objectmodel::BaseObject* obj)
{
    obj->storeResetState();
}

Visitor::Result StoreResetStateVisitor::processNodeTopDown(simulation::Node* node)
{
    for (simulation::Node::ObjectIterator it = node->object.begin(); it != node->object.end(); ++it)
    {
        this->processObject(*it);
    }

    {
        if (!node->nodeInVisualGraph.empty()) node->nodeInVisualGraph->execute<StoreResetStateVisitor>(this->params);
        for (simulation::Node::ChildIterator itChild = node->childInVisualGraph.begin(); itChild != node->childInVisualGraph.end(); ++itChild)
        {
            simulation::Node *child=*itChild;
            child->execute<StoreResetStateVisitor>(this->params);
        }
    }
    return RESULT_CONTINUE;
}

void StoreResetStateVisitor::processNodeBottomUp(simulation::Node* /*node*/)
{
}

} // namespace simulation

} // namespace sofa

<|endoftext|>


<commit_before>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbPerBandVectorImageFilter.h"
#include "itkDiscreteGaussianImageFilter.h"
#include "itkShrinkImageFilter.h"

#include "otbWrapperParameter.h"
#include "otbWrapperOutputImageParameter.h"

namespace otb
{
namespace Wrapper
{

class MultiResolutionPyramid : public Application
{

public:
  /** Standard class typedefs. */
  typedef MultiResolutionPyramid        Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(MultiResolutionPyramid, otb::Application);

  /** Image and filters typedef */
  typedef itk::DiscreteGaussianImageFilter<FloatImageType,
                                           FloatImageType>         SmoothingImageFilterType;

  typedef otb::PerBandVectorImageFilter<FloatVectorImageType,
                                        FloatVectorImageType,
                                        SmoothingImageFilterType>   SmoothingVectorImageFilterType;

  typedef itk::ShrinkImageFilter<FloatVectorImageType,
                                 FloatVectorImageType>              ShrinkFilterType;

private:
  MultiResolutionPyramid()
  {
    SetName("MultiResolutionPyramid");
    SetDescription("Build a multi-resolution pyramid of the image.");

    // Documentation
    SetDocName("Multi Resolution Pyramid Application");
        SetDocLongDescription("This application builds a multi-resolution pyramid of the input image.");
        SetDocLimitations("None");
        SetDocAuthors("OTB-Team");
        SetDocSeeAlso(" ");
        SetDocCLExample("otbApplicationLauncherCommandLine MultiResolutionPyramid ${OTB-BIN}/bin "
            "--in ${OTB-Data}/Input/QB_Toulouse_Ortho_XS.tif  --out multiResolutionImage.tif --level 1 --sfactor 2 --vfactor 0.6 --fast false");
        AddDocTag("Conversion");
        AddDocTag("Image Manipulation");
  }

  void DoCreateParameters()
  {
    AddParameter(ParameterType_InputImage, "in", "Input Image");

    AddParameter(ParameterType_Filename, "out", "Output Image");
    SetParameterDescription("out","will be used to get the prefix and the extension of the images to write");
    SetParameterRole("out",Role_Output);

    AddParameter(ParameterType_Int, "level", "Number Of Levels");
    SetParameterInt("level", 1);
    SetParameterDescription( "level", "Number of levels in the pyramid (default is 1).");

    AddParameter(ParameterType_Int, "sfactor", "Subsampling factor");
    SetParameterInt("sfactor", 2);

    AddParameter(ParameterType_Float,  "vfactor", "Subsampling factor");
    SetParameterFloat("vfactor", 0.6);

    // Boolean Fast scheme
    AddParameter(ParameterType_Empty, "fast", "Use Fast Scheme");
    std::ostringstream desc;
    desc<<"If used, this option allows to speed-up computation by iteratively"
        <<" subsampling previous level of pyramid instead of processing the full input.";
    SetParameterDescription("fast", desc.str());
    MandatoryOff("fast");
  }

  void DoUpdateParameters()
  {
    // Nothing to do here for the parameters : all are independent

    // Reinitialize the internal process used
  }

  void DoExecute()
  {
    // Initializing the process
    m_SmoothingFilter =  SmoothingVectorImageFilterType::New();
    m_ShrinkFilter    = ShrinkFilterType::New();

    // Extract Parameters
    unsigned int nbLevels     = GetParameterInt("level");
    unsigned int shrinkFactor = GetParameterInt("sfactor");
    double varianceFactor     = GetParameterFloat("vfactor");

    bool fastScheme = IsParameterEnabled("fast");

    // Get the input image
    FloatVectorImageType::Pointer inImage = GetParameterImage("in");

    // Get the Initial Output Image FileName
    std::string path, fname, ext;
    std::string ofname = GetParameterString("out");

    // Get the extension and the prefix of the filename
    path  = itksys::SystemTools::GetFilenamePath(ofname);
    fname = itksys::SystemTools::GetFilenameWithoutExtension(ofname);
    ext   = itksys::SystemTools::GetFilenameExtension(ofname);

    unsigned int currentLevel = 1;
    unsigned int currentFactor = shrinkFactor;

    while(currentLevel <= nbLevels)
      {
      otbAppLogDEBUG( << "Processing level " << currentLevel
                      << " with shrink factor "<<currentFactor);

      m_SmoothingFilter->SetInput(inImage);

      // According to
      // http://www.ipol.im/pub/algo/gjmr_line_segment_detector/
      // This is a good balance between blur and aliasing
      double variance = varianceFactor * static_cast<double>(currentFactor);
      m_SmoothingFilter->GetFilter()->SetVariance(variance);

      m_ShrinkFilter->SetInput(m_SmoothingFilter->GetOutput());
      m_ShrinkFilter->SetShrinkFactors(currentFactor);

      if(!fastScheme)
        {
        currentFactor *= shrinkFactor;
        }
      else
        {
        std::cout <<"fast scheme enabled : not implemented for the moment " << std::endl;
        }

      // Create an output parameter to write the current output image
      OutputImageParameter::Pointer paramOut = OutputImageParameter::New();

      // build the current image filename
      std::ostringstream oss;
      oss <<path<<"/"<<fname<<"_"<<currentLevel<<ext;

      // writer label
      std::ostringstream osswriter;
      osswriter<< "writer (level "<< currentLevel<<")";

      // Set the filename of the current output image
      paramOut->SetFileName(oss.str());
      paramOut->SetValue(m_ShrinkFilter->GetOutput());
      // Add the current level to be written
      paramOut->InitializeWriters();
      AddProcess(paramOut->GetWriter(),osswriter.str());
      paramOut->Write();

      ++currentLevel;
      }
  }

  SmoothingVectorImageFilterType::Pointer   m_SmoothingFilter;
  ShrinkFilterType::Pointer                 m_ShrinkFilter;
};
}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::MultiResolutionPyramid)
<commit_msg>DOC: indent change.<commit_after>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbPerBandVectorImageFilter.h"
#include "itkDiscreteGaussianImageFilter.h"
#include "itkShrinkImageFilter.h"

#include "otbWrapperParameter.h"
#include "otbWrapperOutputImageParameter.h"

namespace otb
{
namespace Wrapper
{

class MultiResolutionPyramid : public Application
{

public:
  /** Standard class typedefs. */
  typedef MultiResolutionPyramid        Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(MultiResolutionPyramid, otb::Application);

  /** Image and filters typedef */
  typedef itk::DiscreteGaussianImageFilter<FloatImageType,
                                           FloatImageType>         SmoothingImageFilterType;

  typedef otb::PerBandVectorImageFilter<FloatVectorImageType,
                                        FloatVectorImageType,
                                        SmoothingImageFilterType>   SmoothingVectorImageFilterType;

  typedef itk::ShrinkImageFilter<FloatVectorImageType,
                                 FloatVectorImageType>              ShrinkFilterType;

private:
  MultiResolutionPyramid()
  {
    SetName("MultiResolutionPyramid");
    SetDescription("Build a multi-resolution pyramid of the image.");

    // Documentation
    SetDocName("Multi Resolution Pyramid Application");
    SetDocLongDescription("This application builds a multi-resolution pyramid of the input image.");
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso(" ");
    SetDocCLExample(
                    "otbApplicationLauncherCommandLine MultiResolutionPyramid ${OTB-BIN}/bin "
                      "--in ${OTB-Data}/Input/QB_Toulouse_Ortho_XS.tif  --out multiResolutionImage.tif --level 1 --sfactor 2 --vfactor 0.6 --fast false");
    AddDocTag("Conversion");
    AddDocTag("Image Manipulation");
    AddDocTag("Util");
  }

  void DoCreateParameters()
  {
    AddParameter(ParameterType_InputImage, "in", "Input Image");

    AddParameter(ParameterType_Filename, "out", "Output Image");
    SetParameterDescription("out","will be used to get the prefix and the extension of the images to write");
    SetParameterRole("out",Role_Output);

    AddParameter(ParameterType_Int, "level", "Number Of Levels");
    SetParameterInt("level", 1);
    SetParameterDescription( "level", "Number of levels in the pyramid (default is 1).");

    AddParameter(ParameterType_Int, "sfactor", "Subsampling factor");
    SetParameterInt("sfactor", 2);

    AddParameter(ParameterType_Float,  "vfactor", "Subsampling factor");
    SetParameterFloat("vfactor", 0.6);

    // Boolean Fast scheme
    AddParameter(ParameterType_Empty, "fast", "Use Fast Scheme");
    std::ostringstream desc;
    desc<<"If used, this option allows to speed-up computation by iteratively"
        <<" subsampling previous level of pyramid instead of processing the full input.";
    SetParameterDescription("fast", desc.str());
    MandatoryOff("fast");
  }

  void DoUpdateParameters()
  {
    // Nothing to do here for the parameters : all are independent

    // Reinitialize the internal process used
  }

  void DoExecute()
  {
    // Initializing the process
    m_SmoothingFilter =  SmoothingVectorImageFilterType::New();
    m_ShrinkFilter    = ShrinkFilterType::New();

    // Extract Parameters
    unsigned int nbLevels     = GetParameterInt("level");
    unsigned int shrinkFactor = GetParameterInt("sfactor");
    double varianceFactor     = GetParameterFloat("vfactor");

    bool fastScheme = IsParameterEnabled("fast");

    // Get the input image
    FloatVectorImageType::Pointer inImage = GetParameterImage("in");

    // Get the Initial Output Image FileName
    std::string path, fname, ext;
    std::string ofname = GetParameterString("out");

    // Get the extension and the prefix of the filename
    path  = itksys::SystemTools::GetFilenamePath(ofname);
    fname = itksys::SystemTools::GetFilenameWithoutExtension(ofname);
    ext   = itksys::SystemTools::GetFilenameExtension(ofname);

    unsigned int currentLevel = 1;
    unsigned int currentFactor = shrinkFactor;

    while(currentLevel <= nbLevels)
      {
      otbAppLogDEBUG( << "Processing level " << currentLevel
                      << " with shrink factor "<<currentFactor);

      m_SmoothingFilter->SetInput(inImage);

      // According to
      // http://www.ipol.im/pub/algo/gjmr_line_segment_detector/
      // This is a good balance between blur and aliasing
      double variance = varianceFactor * static_cast<double>(currentFactor);
      m_SmoothingFilter->GetFilter()->SetVariance(variance);

      m_ShrinkFilter->SetInput(m_SmoothingFilter->GetOutput());
      m_ShrinkFilter->SetShrinkFactors(currentFactor);

      if(!fastScheme)
        {
        currentFactor *= shrinkFactor;
        }
      else
        {
        std::cout <<"fast scheme enabled : not implemented for the moment " << std::endl;
        }

      // Create an output parameter to write the current output image
      OutputImageParameter::Pointer paramOut = OutputImageParameter::New();

      // build the current image filename
      std::ostringstream oss;
      oss <<path<<"/"<<fname<<"_"<<currentLevel<<ext;

      // writer label
      std::ostringstream osswriter;
      osswriter<< "writer (level "<< currentLevel<<")";

      // Set the filename of the current output image
      paramOut->SetFileName(oss.str());
      paramOut->SetValue(m_ShrinkFilter->GetOutput());
      // Add the current level to be written
      paramOut->InitializeWriters();
      AddProcess(paramOut->GetWriter(),osswriter.str());
      paramOut->Write();

      ++currentLevel;
      }
  }

  SmoothingVectorImageFilterType::Pointer   m_SmoothingFilter;
  ShrinkFilterType::Pointer                 m_ShrinkFilter;
};
}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::MultiResolutionPyramid)
<|endoftext|>


<commit_before>/*
 * Chrome Token Signing Native Host
 *
 * This library 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 library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "NativeSigner.h"
#include "HostExceptions.h"
#include "Logger.h"

#include <Windows.h>
#include <ncrypt.h>
#include <WinCrypt.h>
#include <cryptuiapi.h>

#include <memory>

using namespace std;

vector<unsigned char> NativeSigner::sign(const vector<unsigned char> &digest)
{
	BCRYPT_PKCS1_PADDING_INFO padInfo;
	DWORD obtainKeyStrategy = CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;

	ALG_ID alg = 0;
	switch (digest.size())
	{
	case BINARY_SHA1_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA1_ALGORITHM;
		alg = CALG_SHA1;
		break;
	case BINARY_SHA224_LENGTH:
		padInfo.pszAlgId = L"SHA224";
		obtainKeyStrategy = CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
		break;
	case BINARY_SHA256_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA256_ALGORITHM;
		alg = CALG_SHA_256;
		break;
	case BINARY_SHA384_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA384_ALGORITHM;
		alg = CALG_SHA_384;
		break;
	case BINARY_SHA512_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA512_ALGORITHM;
		alg = CALG_SHA_512;
		break;
	default:
		throw InvalidHashException();
	}

	HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_SYSTEM,
		X509_ASN_ENCODING, 0, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_READONLY_FLAG, L"MY");
	if (!store)
		throw TechnicalException("Failed to open Cert Store");

	PCCERT_CONTEXT certFromBinary = CertCreateCertificateContext(X509_ASN_ENCODING, cert.data(), cert.size());
	PCCERT_CONTEXT certInStore = CertFindCertificateInStore(store, X509_ASN_ENCODING, 0, CERT_FIND_EXISTING, certFromBinary, 0);
	CertFreeCertificateContext(certFromBinary);
	CertCloseStore(store, 0);

	if (!certInStore)
		throw NoCertificatesException();

	DWORD flags = obtainKeyStrategy | CRYPT_ACQUIRE_COMPARE_KEY_FLAG;
	DWORD spec = 0;
	BOOL freeKeyHandle = false;
	HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *handle = new HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0);
	BOOL gotKey = CryptAcquireCertificatePrivateKey(certInStore, flags, 0, handle, &spec, &freeKeyHandle);
	CertFreeCertificateContext(certInStore);
	if (!gotKey)
		throw NoCertificatesException();

	auto deleter = [=](HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *key) {
		if (!freeKeyHandle)
			return;
		if (spec == CERT_NCRYPT_KEY_SPEC)
			NCryptFreeObject(*key);
		else
			CryptReleaseContext(*key, 0);
	};
	unique_ptr<HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, decltype(deleter)> key(handle, deleter);

	SECURITY_STATUS err = ERROR_SUCCESS;
	vector<unsigned char> signature;
	switch (spec)
	{
	case CERT_NCRYPT_KEY_SPEC:
	{
		DWORD size = 0;
		err = NCryptSignHash(*key.get(), &padInfo, PBYTE(digest.data()), DWORD(digest.size()),
			nullptr, 0, LPDWORD(&size), BCRYPT_PAD_PKCS1);
		if (FAILED(err))
			break;

		signature.resize(size);
		err = NCryptSignHash(*key.get(), &padInfo, PBYTE(digest.data()), DWORD(digest.size()),
			signature.data(), DWORD(signature.size()), LPDWORD(&size), BCRYPT_PAD_PKCS1);
		break;
	}
	case AT_KEYEXCHANGE:
	case AT_SIGNATURE:
	{
		HCRYPTHASH hash = 0;
		if (!CryptCreateHash(*key.get(), alg, 0, 0, &hash))
			throw TechnicalException("CreateHash failed");
		if (!CryptSetHashParam(hash, HP_HASHVAL, digest.data(), 0))	{
			CryptDestroyHash(hash);
			throw TechnicalException("SetHashParam failed");
		}

		DWORD size = 0;
		if (!CryptSignHashW(hash, spec, nullptr, 0, nullptr, &size)) {
			CryptDestroyHash(hash);
			err = GetLastError();
			break;
		}

		signature.resize(size);
		if (!CryptSignHashW(hash, spec, nullptr, 0, LPBYTE(signature.data()), &size))
			err = GetLastError();
		CryptDestroyHash(hash);
		reverse(signature.begin(), signature.end());
		break;
	}
	default:
		throw TechnicalException("Incompatible key");
	}

	_log("sign return code: %x", err);
	switch (err)
	{
	case ERROR_SUCCESS:
		return signature;
	case SCARD_W_CANCELLED_BY_USER:
	case ERROR_CANCELLED:
		throw UserCancelledException("Signing was cancelled");
	case SCARD_W_CHV_BLOCKED:
		throw PinBlockedException();
	case NTE_INVALID_HANDLE:
		throw TechnicalException("The supplied handle is invalid");
	default:
		throw TechnicalException("Signing failed");
	}
}

<commit_msg>CNG MS SmartCard Provider is strict with RSA parameters allowed only with RSA  key (#64)<commit_after>/*
 * Chrome Token Signing Native Host
 *
 * This library 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 library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "NativeSigner.h"
#include "HostExceptions.h"
#include "Logger.h"

#include <Windows.h>
#include <ncrypt.h>
#include <WinCrypt.h>
#include <cryptuiapi.h>

#include <memory>

using namespace std;

vector<unsigned char> NativeSigner::sign(const vector<unsigned char> &digest)
{
	BCRYPT_PKCS1_PADDING_INFO padInfo;
	DWORD obtainKeyStrategy = CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG;

	ALG_ID alg = 0;
	switch (digest.size())
	{
	case BINARY_SHA1_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA1_ALGORITHM;
		alg = CALG_SHA1;
		break;
	case BINARY_SHA224_LENGTH:
		padInfo.pszAlgId = L"SHA224";
		obtainKeyStrategy = CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
		break;
	case BINARY_SHA256_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA256_ALGORITHM;
		alg = CALG_SHA_256;
		break;
	case BINARY_SHA384_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA384_ALGORITHM;
		alg = CALG_SHA_384;
		break;
	case BINARY_SHA512_LENGTH:
		padInfo.pszAlgId = NCRYPT_SHA512_ALGORITHM;
		alg = CALG_SHA_512;
		break;
	default:
		throw InvalidHashException();
	}

	HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_SYSTEM,
		X509_ASN_ENCODING, 0, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_READONLY_FLAG, L"MY");
	if (!store)
		throw TechnicalException("Failed to open Cert Store");

	PCCERT_CONTEXT certFromBinary = CertCreateCertificateContext(X509_ASN_ENCODING, cert.data(), cert.size());
	PCCERT_CONTEXT certInStore = CertFindCertificateInStore(store, X509_ASN_ENCODING, 0, CERT_FIND_EXISTING, certFromBinary, 0);
	CertFreeCertificateContext(certFromBinary);
	CertCloseStore(store, 0);

	if (!certInStore)
		throw NoCertificatesException();

	DWORD flags = obtainKeyStrategy | CRYPT_ACQUIRE_COMPARE_KEY_FLAG;
	DWORD spec = 0;
	BOOL freeKeyHandle = false;
	HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *handle = new HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0);
	BOOL gotKey = CryptAcquireCertificatePrivateKey(certInStore, flags, 0, handle, &spec, &freeKeyHandle);
	CertFreeCertificateContext(certInStore);
	if (!gotKey)
		throw NoCertificatesException();

	auto deleter = [=](HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *key) {
		if (!freeKeyHandle)
			return;
		if (spec == CERT_NCRYPT_KEY_SPEC)
			NCryptFreeObject(*key);
		else
			CryptReleaseContext(*key, 0);
	};
	unique_ptr<HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, decltype(deleter)> key(handle, deleter);

	SECURITY_STATUS err = ERROR_SUCCESS;
	vector<unsigned char> signature;
	switch (spec)
	{
	case CERT_NCRYPT_KEY_SPEC:
	{
		DWORD size = 0;
		wstring algo(5, 0);
		err = NCryptGetProperty(*key.get(), NCRYPT_ALGORITHM_GROUP_PROPERTY, PBYTE(algo.data()), (algo.size() + 1) * 2, &size, 0);
		algo.resize(size / 2 - 1);
		bool isRSA = algo == L"RSA";

		err = NCryptSignHash(*key.get(), isRSA ? &padInfo : nullptr, PBYTE(digest.data()), DWORD(digest.size()),
			nullptr, 0, LPDWORD(&size), isRSA ? BCRYPT_PAD_PKCS1 : 0);
		if (FAILED(err))
			break;

		signature.resize(size);
		err = NCryptSignHash(*key.get(), isRSA ? &padInfo : nullptr, PBYTE(digest.data()), DWORD(digest.size()),
			signature.data(), DWORD(signature.size()), LPDWORD(&size), isRSA ? BCRYPT_PAD_PKCS1 : 0);
		break;
	}
	case AT_KEYEXCHANGE:
	case AT_SIGNATURE:
	{
		HCRYPTHASH hash = 0;
		if (!CryptCreateHash(*key.get(), alg, 0, 0, &hash))
			throw TechnicalException("CreateHash failed");
		if (!CryptSetHashParam(hash, HP_HASHVAL, digest.data(), 0))	{
			CryptDestroyHash(hash);
			throw TechnicalException("SetHashParam failed");
		}

		DWORD size = 0;
		if (!CryptSignHashW(hash, spec, nullptr, 0, nullptr, &size)) {
			CryptDestroyHash(hash);
			err = GetLastError();
			break;
		}

		signature.resize(size);
		if (!CryptSignHashW(hash, spec, nullptr, 0, LPBYTE(signature.data()), &size))
			err = GetLastError();
		CryptDestroyHash(hash);
		reverse(signature.begin(), signature.end());
		break;
	}
	default:
		throw TechnicalException("Incompatible key");
	}

	_log("sign return code: %x", err);
	switch (err)
	{
	case ERROR_SUCCESS:
		return signature;
	case SCARD_W_CANCELLED_BY_USER:
	case ERROR_CANCELLED:
		throw UserCancelledException("Signing was cancelled");
	case SCARD_W_CHV_BLOCKED:
		throw PinBlockedException();
	case NTE_INVALID_HANDLE:
		throw TechnicalException("The supplied handle is invalid");
	default:
		throw TechnicalException("Signing failed");
	}
}

<|endoftext|>


<commit_before>#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "device.hpp"
#include "context.hpp"
#include "global_managers_init.hpp"
#include <stdlib.h>
#include <cmath>

#ifndef _WIN32
#include <unistd.h>
#endif

using namespace Vulkan;

static void check_gl_error()
{
	GLenum err;
	if ((err = glGetError()) != GL_NO_ERROR)
	{
		LOGE("GL error: #%x.\n", err);
		exit(EXIT_FAILURE);
	}
}

static void import_semaphore(GLuint &glsem, const ExternalHandle &handle)
{
	glGenSemaphoresEXT(1, &glsem);
#ifdef _WIN32
	glImportSemaphoreWin32HandleEXT(glsem, GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, exported_semaphore.handle);
	CloseHandle(exported_semaphore.handle);
#else
	// Importing an FD takes ownership of it. We'll reimport the FD, so need to dup it.
	glImportSemaphoreFdEXT(glsem, GL_HANDLE_TYPE_OPAQUE_FD_EXT, handle.handle);
#endif
	check_gl_error();
}

int main()
{
	Granite::Global::init(1, Granite::Global::MANAGER_FEATURE_DEFAULT_BITS);
	if (!glfwInit())
		return EXIT_FAILURE;

	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);

	GLFWwindow *window = glfwCreateWindow(1280, 720, "GL interop", nullptr, nullptr);
	if (!window)
	{
		LOGE("Failed to create window.\n");
		return EXIT_FAILURE;
	}

	glfwMakeContextCurrent(window);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		LOGE("Failed to load GL context functions.\n");
		return EXIT_FAILURE;
	}

	if (!GLAD_GL_EXT_memory_object || !GLAD_GL_EXT_semaphore)
	{
		LOGE("External functions not supported.\n");
		return EXIT_FAILURE;
	}

#ifdef _WIN32
	if (!GLAD_GL_EXT_memory_object_win32 || !GLAD_GL_EXT_semaphore_win32)
	{
		LOGE("External handle functions not supported.\n");
		return EXIT_FAILURE;
	}
#else
	if (!GLAD_GL_EXT_memory_object_fd || !GLAD_GL_EXT_semaphore_fd)
	{
		LOGE("External FD functions not supported.\n");
		return EXIT_FAILURE;
	}
#endif

	glfwSwapInterval(1);
	unsigned frame_count = 0;

	Context ctx;
	Device device;

	if (!Context::init_loader(nullptr))
		return EXIT_FAILURE;

	Context::SystemHandles handles = {};
	handles.filesystem = GRANITE_FILESYSTEM();
	ctx.set_system_handles(handles);
	if (!ctx.init_instance_and_device(nullptr, 0, nullptr, 0))
	{
		LOGE("Failed to create Vulkan device.\n");
		return EXIT_FAILURE;
	}

	device.set_context(ctx);

	if (!device.get_device_features().supports_external)
	{
		LOGE("Vulkan device does not support external.\n");
		return EXIT_FAILURE;
	}

	auto image_info = ImageCreateInfo::render_target(512, 512, VK_FORMAT_R8G8B8A8_UNORM);
	image_info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
	image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
	image_info.misc = IMAGE_MISC_EXTERNAL_MEMORY_BIT;
	auto image = device.create_image(image_info);

	// Export image.
	auto exported_image = image->export_handle();

	// Import image as a texture.
	GLuint gltex;
	GLuint glmem;
	GLuint glfbo;
	glCreateTextures(GL_TEXTURE_2D, 1, &gltex);
	glCreateMemoryObjectsEXT(1, &glmem);
	glCreateFramebuffers(1, &glfbo);

	// We always use dedicated allocations in Granite for external objects.
	GLint gltrue = GL_TRUE;
	glMemoryObjectParameterivEXT(glmem, GL_DEDICATED_MEMORY_OBJECT_EXT, &gltrue);

	const char *vendor = (const char *)glGetString(GL_VENDOR);
	LOGI("GL vendor: %s\n", vendor);

	auto &features = device.get_device_features();
	if (features.id_properties.deviceLUIDValid)
	{
		GLubyte luid[GL_LUID_SIZE_EXT] = {};
		glGetUnsignedBytevEXT(GL_DEVICE_LUID_EXT, luid);

		if (memcmp(features.id_properties.deviceLUID, luid, GL_LUID_SIZE_EXT) != 0)
		{
			LOGE("LUID mismatch.\n");
			return EXIT_FAILURE;
		}
	}

#ifdef _WIN32
	glImportMemoryWin32HandleEXT(glmem, image->get_allocation().get_size(),
	                             GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, exported_image.handle);
	CloseHandle(exported_image.handle);
#else
	// Importing takes ownership of the FD.
	glImportMemoryFdEXT(glmem, image->get_allocation().get_size(),
	                    GL_HANDLE_TYPE_OPAQUE_FD_EXT, exported_image.handle);
#endif

	glTextureStorageMem2DEXT(gltex, 1, GL_RGBA8,
	                         GLsizei(image->get_width()),
	                         GLsizei(image->get_height()),
	                         glmem, 0);

	// We'll blit the result to screen with BlitFramebuffer.
	glNamedFramebufferTexture(glfbo, GL_COLOR_ATTACHMENT0, gltex, 0);

	GLenum status;
	if ((status = glCheckNamedFramebufferStatus(glfbo, GL_READ_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
	{
		LOGE("Failed to bind framebuffer (#%x).\n", status);
		return EXIT_FAILURE;
	}

	while (!glfwWindowShouldClose(window))
	{
		glfwPollEvents();

		// Render frame in Vulkan
		{
			auto cmd = device.request_command_buffer();
			RenderPassInfo rp_info;
			rp_info.num_color_attachments = 1;
			rp_info.color_attachments[0] = &image->get_view();
			rp_info.store_attachments = 1u << 0;
			rp_info.clear_attachments = 1u << 0;
			rp_info.clear_color[0].float32[0] = float(0.5f + 0.3f * sin(double(frame_count) * 0.010));
			rp_info.clear_color[0].float32[1] = float(0.5f + 0.3f * sin(double(frame_count) * 0.020));
			rp_info.clear_color[0].float32[2] = float(0.5f + 0.3f * sin(double(frame_count) * 0.015));

			// Don't need to reacquire from external queue family if we don't care about the contents being preserved.
			cmd->image_barrier(*image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
			                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
			                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);

			cmd->begin_render_pass(rp_info);

			VkClearRect clear_rect = {};
			VkClearValue clear_value = {};

			clear_rect.layerCount = 1;
			clear_rect.rect.extent = { 32, 32 };

			for (unsigned i = 0; i < 4; i++)
				clear_value.color.float32[i] = 1.0f - rp_info.clear_color[0].float32[i];

			for (unsigned i = 0; i < 40 * 5; i += 40)
			{
				clear_rect.rect.offset.x = int(256.0 - 16.0 + 100.0 * cos(double(frame_count + i) * 0.02));
				clear_rect.rect.offset.y = int(256.0 - 16.0 + 100.0 * sin(double(frame_count + i) * 0.02));
				cmd->clear_quad(0, clear_rect, clear_value, VK_IMAGE_ASPECT_COLOR_BIT);
			}

			cmd->end_render_pass();
			cmd->release_external_image_barrier(
			    *image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
			    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
			device.submit(cmd);
		}

		// Import the FD.
		const GLenum gllayout = GL_LAYOUT_COLOR_ATTACHMENT_EXT;
		GLuint glsem;

		{
			// Synchronize with OpenGL. Export a handle.
			auto ext_semaphore = device.request_semaphore_external(
			    VK_SEMAPHORE_TYPE_BINARY_KHR, ExternalHandle::get_opaque_semaphore_handle_type());
			device.submit_empty(CommandBuffer::Type::Generic, nullptr, ext_semaphore.get());
			auto exported_semaphore = ext_semaphore->export_to_handle(ExternalHandle::get_opaque_semaphore_handle_type());

			import_semaphore(glsem, exported_semaphore);

			// Wait. The layout matches whatever we used when releasing the image.
			glWaitSemaphoreEXT(glsem, 0, nullptr, 1, &gltex, &gllayout);
			glDeleteSemaphoresEXT(1, &glsem);
		}

		int fb_width, fb_height;
		glfwGetFramebufferSize(window, &fb_width, &fb_height);

		glBlitNamedFramebuffer(glfbo, 0,
		                       0, 0, GLint(image->get_width()), GLint(image->get_height()),
		                       0, 0, fb_width, fb_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);

		// We're done using the semaphore. Import the layout from GL and wait on it to avoid write-after-read hazard.
		// We could keep reusing the semaphore, but NV Linux seems to trigger random ~5 second hangs
		// when doing that for some reason, so just use one semaphore per signal wait pair.
		{
			// Synchronize with OpenGL. Export a handle that GL can signal.
			auto ext_semaphore = device.request_semaphore_external(
			    VK_SEMAPHORE_TYPE_BINARY_KHR, ExternalHandle::get_opaque_semaphore_handle_type());
			// Have to mark the semaphore is signalled since we assert on that being the case when exporting a semaphore.
			ext_semaphore->signal_external();
			auto exported_semaphore = ext_semaphore->export_to_handle(ExternalHandle::get_opaque_semaphore_handle_type());

			import_semaphore(glsem, exported_semaphore);

			glSignalSemaphoreEXT(glsem, 0, nullptr, 1, &gltex, &gllayout);

			// Unsure if we have to flush to make sure the signal has been processed.
			glFlush();

			device.add_wait_semaphore(CommandBuffer::Type::Generic, std::move(ext_semaphore),
			                          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, true);

			glDeleteSemaphoresEXT(1, &glsem);
		}

		glfwSwapBuffers(window);
		device.next_frame_context();
		frame_count++;
	}

	glDeleteFramebuffers(1, &glfbo);
	glDeleteTextures(1, &gltex);
	glDeleteMemoryObjectsEXT(1, &glmem);

	check_gl_error();

	glfwDestroyWindow(window);
	glfwTerminate();
}<commit_msg>Fix Windows derps in GL interop test.<commit_after>#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "device.hpp"
#include "context.hpp"
#include "global_managers_init.hpp"
#include <stdlib.h>
#include <cmath>

#ifndef _WIN32
#include <unistd.h>
#endif

using namespace Vulkan;

static void check_gl_error()
{
	GLenum err;
	if ((err = glGetError()) != GL_NO_ERROR)
	{
		LOGE("GL error: #%x.\n", err);
		exit(EXIT_FAILURE);
	}
}

static void import_semaphore(GLuint &glsem, const ExternalHandle &handle)
{
	glGenSemaphoresEXT(1, &glsem);
#ifdef _WIN32
	glImportSemaphoreWin32HandleEXT(glsem, GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, handle.handle);
	CloseHandle(handle.handle);
#else
	// Importing an FD takes ownership of it. We'll reimport the FD, so need to dup it.
	glImportSemaphoreFdEXT(glsem, GL_HANDLE_TYPE_OPAQUE_FD_EXT, handle.handle);
#endif
	check_gl_error();
}

int main()
{
	Granite::Global::init(1, Granite::Global::MANAGER_FEATURE_DEFAULT_BITS);
	if (!glfwInit())
		return EXIT_FAILURE;

	glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);

	GLFWwindow *window = glfwCreateWindow(1280, 720, "GL interop", nullptr, nullptr);
	if (!window)
	{
		LOGE("Failed to create window.\n");
		return EXIT_FAILURE;
	}

	glfwMakeContextCurrent(window);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		LOGE("Failed to load GL context functions.\n");
		return EXIT_FAILURE;
	}

	if (!GLAD_GL_EXT_memory_object || !GLAD_GL_EXT_semaphore)
	{
		LOGE("External functions not supported.\n");
		return EXIT_FAILURE;
	}

#ifdef _WIN32
	if (!GLAD_GL_EXT_memory_object_win32 || !GLAD_GL_EXT_semaphore_win32)
	{
		LOGE("External handle functions not supported.\n");
		return EXIT_FAILURE;
	}
#else
	if (!GLAD_GL_EXT_memory_object_fd || !GLAD_GL_EXT_semaphore_fd)
	{
		LOGE("External FD functions not supported.\n");
		return EXIT_FAILURE;
	}
#endif

	glfwSwapInterval(1);
	unsigned frame_count = 0;

	Context ctx;
	Device device;

	if (!Context::init_loader(nullptr))
		return EXIT_FAILURE;

	Context::SystemHandles handles = {};
	handles.filesystem = GRANITE_FILESYSTEM();
	ctx.set_system_handles(handles);
	if (!ctx.init_instance_and_device(nullptr, 0, nullptr, 0))
	{
		LOGE("Failed to create Vulkan device.\n");
		return EXIT_FAILURE;
	}

	device.set_context(ctx);

	if (!device.get_device_features().supports_external)
	{
		LOGE("Vulkan device does not support external.\n");
		return EXIT_FAILURE;
	}

	auto image_info = ImageCreateInfo::render_target(512, 512, VK_FORMAT_R8G8B8A8_UNORM);
	image_info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
	image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
	image_info.misc = IMAGE_MISC_EXTERNAL_MEMORY_BIT;
	auto image = device.create_image(image_info);

	// Export image.
	auto exported_image = image->export_handle();

	// Import image as a texture.
	GLuint gltex;
	GLuint glmem;
	GLuint glfbo;
	glCreateTextures(GL_TEXTURE_2D, 1, &gltex);
	glCreateMemoryObjectsEXT(1, &glmem);
	glCreateFramebuffers(1, &glfbo);

	// We always use dedicated allocations in Granite for external objects.
	GLint gltrue = GL_TRUE;
	glMemoryObjectParameterivEXT(glmem, GL_DEDICATED_MEMORY_OBJECT_EXT, &gltrue);

	check_gl_error();

	const char *vendor = (const char *)glGetString(GL_VENDOR);
	LOGI("GL vendor: %s\n", vendor);

	auto &features = device.get_device_features();
	if (features.id_properties.deviceLUIDValid)
	{
		GLubyte luid[GL_LUID_SIZE_EXT] = {};
		glGetUnsignedBytevEXT(GL_DEVICE_LUID_EXT, luid);

		if (memcmp(features.id_properties.deviceLUID, luid, GL_LUID_SIZE_EXT) != 0)
		{
			LOGE("LUID mismatch.\n");
			return EXIT_FAILURE;
		}
	}

#ifdef _WIN32
	glImportMemoryWin32HandleEXT(glmem, image->get_allocation().get_size(),
	                             GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, exported_image.handle);
#else
	// Importing takes ownership of the FD.
	glImportMemoryFdEXT(glmem, image->get_allocation().get_size(),
	                    GL_HANDLE_TYPE_OPAQUE_FD_EXT, exported_image.handle);
#endif

	check_gl_error();

	glTextureStorageMem2DEXT(gltex, 1, GL_RGBA8,
	                         GLsizei(image->get_width()),
	                         GLsizei(image->get_height()),
	                         glmem, 0);

#ifdef _WIN32
	// The HANDLE seems to be consumed at TextureStorage time, otherwise we get OUT_OF_MEMORY error on NV Windows.
	// Sort of makes sense since it's a dedicated allocation?
	CloseHandle(exported_image.handle);
#endif

	// We'll blit the result to screen with BlitFramebuffer.
	glNamedFramebufferTexture(glfbo, GL_COLOR_ATTACHMENT0, gltex, 0);

	GLenum status;
	if ((status = glCheckNamedFramebufferStatus(glfbo, GL_READ_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
	{
		LOGE("Failed to bind framebuffer (#%x).\n", status);
		return EXIT_FAILURE;
	}

	while (!glfwWindowShouldClose(window))
	{
		glfwPollEvents();

		// Render frame in Vulkan
		{
			auto cmd = device.request_command_buffer();
			RenderPassInfo rp_info;
			rp_info.num_color_attachments = 1;
			rp_info.color_attachments[0] = &image->get_view();
			rp_info.store_attachments = 1u << 0;
			rp_info.clear_attachments = 1u << 0;
			rp_info.clear_color[0].float32[0] = float(0.5f + 0.3f * sin(double(frame_count) * 0.010));
			rp_info.clear_color[0].float32[1] = float(0.5f + 0.3f * sin(double(frame_count) * 0.020));
			rp_info.clear_color[0].float32[2] = float(0.5f + 0.3f * sin(double(frame_count) * 0.015));

			// Don't need to reacquire from external queue family if we don't care about the contents being preserved.
			cmd->image_barrier(*image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
			                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
			                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);

			cmd->begin_render_pass(rp_info);

			VkClearRect clear_rect = {};
			VkClearValue clear_value = {};

			clear_rect.layerCount = 1;
			clear_rect.rect.extent = { 32, 32 };

			for (unsigned i = 0; i < 4; i++)
				clear_value.color.float32[i] = 1.0f - rp_info.clear_color[0].float32[i];

			for (unsigned i = 0; i < 40 * 5; i += 40)
			{
				clear_rect.rect.offset.x = int(256.0 - 16.0 + 100.0 * cos(double(frame_count + i) * 0.02));
				clear_rect.rect.offset.y = int(256.0 - 16.0 + 100.0 * sin(double(frame_count + i) * 0.02));
				cmd->clear_quad(0, clear_rect, clear_value, VK_IMAGE_ASPECT_COLOR_BIT);
			}

			cmd->end_render_pass();
			cmd->release_external_image_barrier(
			    *image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
			    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
			device.submit(cmd);
		}

		// Import the FD.
		const GLenum gllayout = GL_LAYOUT_COLOR_ATTACHMENT_EXT;
		GLuint glsem;

		{
			// Synchronize with OpenGL. Export a handle.
			auto ext_semaphore = device.request_semaphore_external(
			    VK_SEMAPHORE_TYPE_BINARY_KHR, ExternalHandle::get_opaque_semaphore_handle_type());
			device.submit_empty(CommandBuffer::Type::Generic, nullptr, ext_semaphore.get());
			auto exported_semaphore = ext_semaphore->export_to_handle(ExternalHandle::get_opaque_semaphore_handle_type());

			import_semaphore(glsem, exported_semaphore);

			// Wait. The layout matches whatever we used when releasing the image.
			glWaitSemaphoreEXT(glsem, 0, nullptr, 1, &gltex, &gllayout);
			glDeleteSemaphoresEXT(1, &glsem);
		}

		int fb_width, fb_height;
		glfwGetFramebufferSize(window, &fb_width, &fb_height);

		glBlitNamedFramebuffer(glfbo, 0,
		                       0, 0, GLint(image->get_width()), GLint(image->get_height()),
		                       0, 0, fb_width, fb_height, GL_COLOR_BUFFER_BIT, GL_LINEAR);

		// We're done using the semaphore. Import the layout from GL and wait on it to avoid write-after-read hazard.
		// We could keep reusing the semaphore, but NV Linux seems to trigger random ~5 second hangs
		// when doing that for some reason, so just use one semaphore per signal wait pair.
		{
			// Synchronize with OpenGL. Export a handle that GL can signal.
			auto ext_semaphore = device.request_semaphore_external(
			    VK_SEMAPHORE_TYPE_BINARY_KHR, ExternalHandle::get_opaque_semaphore_handle_type());
			// Have to mark the semaphore is signalled since we assert on that being the case when exporting a semaphore.
			ext_semaphore->signal_external();
			auto exported_semaphore = ext_semaphore->export_to_handle(ExternalHandle::get_opaque_semaphore_handle_type());

			import_semaphore(glsem, exported_semaphore);

			glSignalSemaphoreEXT(glsem, 0, nullptr, 1, &gltex, &gllayout);

			// Unsure if we have to flush to make sure the signal has been processed.
			glFlush();

			device.add_wait_semaphore(CommandBuffer::Type::Generic, std::move(ext_semaphore),
			                          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, true);

			glDeleteSemaphoresEXT(1, &glsem);
		}

		glfwSwapBuffers(window);
		device.next_frame_context();
		frame_count++;
	}

	glDeleteFramebuffers(1, &glfbo);
	glDeleteTextures(1, &gltex);
	glDeleteMemoryObjectsEXT(1, &glmem);

	check_gl_error();

	glfwDestroyWindow(window);
	glfwTerminate();
}<|endoftext|>