<commit_before>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "value_codec.h"
#include "codec.h"
#include <vespa/vespalib/util/typify.h>

using namespace vespalib::eval::codec;

namespace vespalib::eval {

void encode_mapped_labels(nbostream &output, size_t num_mapped_dims, const std::vector<vespalib::stringref> &addr) {
    for (size_t i = 0; i < num_mapped_dims; ++i) {
        output.writeSmallString(addr[i]);
    }
}

void decode_mapped_labels(nbostream &input, size_t num_mapped_dims, std::vector<vespalib::string> &addr) {
    for (size_t i = 0; i < num_mapped_dims; ++i) {
        vespalib::string name;
        input.readSmallString(name);
        addr[i] = name;
    }
}

void new_encode(const Value &value, nbostream &output) {
    TypeMeta meta(value.type());
    Format format(meta);
    output.putInt1_4Bytes(format.tag);
    encode_type(output, format, value.type(), meta);
    maybe_encode_num_blocks(output, meta, value.cells().size / meta.block_size);
    std::vector<vespalib::stringref> address(meta.mapped.size());
    std::vector<vespalib::stringref*> a_refs(meta.mapped.size());
    for (size_t i = 0; i < meta.mapped.size(); ++i) {
        a_refs[i] = &address[i];
    }
    auto view = value.index().create_view({});
    view->lookup({});
    size_t subspace;
    while (view->next_result(a_refs, subspace)) {
        encode_mapped_labels(output, meta.mapped.size(), address);
        if (meta.cell_type == CellType::FLOAT) {
            auto iter = value.cells().typify<float>().begin();
            iter += (subspace * meta.block_size);
            for (size_t i = 0; i < meta.block_size; ++i) {
                output << (float) *iter++;
            }
        } else {
            auto iter = value.cells().typify<double>().begin();
            iter += (subspace * meta.block_size);
            for (size_t i = 0; i < meta.block_size; ++i) {
                output << *iter++;
            }
        }
    }
}

template<typename T>
void decode_cells(nbostream &input, size_t num_cells, ArrayRef<T> dst)
{
    T value;
    for (size_t i = 0; i < num_cells; ++i) {
        input >> value;
        dst[i] = value;
    }
}

struct DecodeState {
    const ValueType &type;
    const size_t block_size;
    const size_t num_blocks;
    const size_t num_mapped_dims;
    std::vector<vespalib::string> address;
    std::vector<vespalib::stringref> addr_refs;
    DecodeState(const ValueType &type_in,
                size_t block_size_in,
                size_t num_blocks_in,
                size_t num_mapped_dims_in)
      : type(type_in),
        block_size(block_size_in),
        num_blocks(num_blocks_in),
        num_mapped_dims(num_mapped_dims_in),
        address(num_mapped_dims_in),
        addr_refs(num_mapped_dims_in)
    {}
    void fix_refs() {
        for (size_t j = 0; j < num_mapped_dims; ++j) {
            addr_refs[j] = address[j];
        }
    }
};

struct ContentDecoder {
    template<typename T>
    static std::unique_ptr<Value> invoke(nbostream &input, DecodeState &state, const ValueBuilderFactory &factory) {
        auto builder = factory.create_value_builder<T>(state.type, state.num_mapped_dims, state.block_size, state.num_blocks);
        for (size_t i = 0; i < state.num_blocks; ++i) {
            decode_mapped_labels(input, state.num_mapped_dims, state.address);
            state.fix_refs();
            auto block_cells = builder->add_subspace(state.addr_refs);
            decode_cells(input, state.block_size, block_cells);
        }
        return builder->build(std::move(builder));
    }
};

std::unique_ptr<Value> new_decode(nbostream &input, const ValueBuilderFactory &factory) {
    Format format(input.getInt1_4Bytes());
    ValueType type = decode_type(input, format);
    TypeMeta meta(type);
    const size_t num_blocks = maybe_decode_num_blocks(input, meta, format);
    DecodeState state(type, meta.block_size, num_blocks, meta.mapped.size());
    return typify_invoke<1,TypifyCellType,ContentDecoder>(meta.cell_type, input, state, factory);
}

} // namespace
<commit_msg>peek into nbostream instead of copying strings<commit_after>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "value_codec.h"
#include "codec.h"
#include <vespa/vespalib/util/typify.h>

using namespace vespalib::eval::codec;

namespace vespalib::eval {

namespace {

void encode_mapped_labels(nbostream &output, size_t num_mapped_dims, const std::vector<vespalib::stringref> &addr) {
    for (size_t i = 0; i < num_mapped_dims; ++i) {
        output.writeSmallString(addr[i]);
    }
}

void decode_mapped_labels(nbostream &input, size_t num_mapped_dims, std::vector<vespalib::stringref> &addr) {
    for (size_t i = 0; i < num_mapped_dims; ++i) {
        size_t strSize = input.getInt1_4Bytes();
        addr[i] = vespalib::stringref(input.peek(), strSize);
        input.adjustReadPos(strSize);
    }
}

template<typename T>
void decode_cells(nbostream &input, size_t num_cells, ArrayRef<T> dst)
{
    T value;
    for (size_t i = 0; i < num_cells; ++i) {
        input >> value;
        dst[i] = value;
    }
}

struct DecodeState {
    const ValueType &type;
    const size_t block_size;
    const size_t num_blocks;
    const size_t num_mapped_dims;
};

struct ContentDecoder {
    template<typename T>
    static std::unique_ptr<Value> invoke(nbostream &input, const DecodeState &state, const ValueBuilderFactory &factory) {
        std::vector<vespalib::stringref> address(state.num_mapped_dims);
        auto builder = factory.create_value_builder<T>(state.type, state.num_mapped_dims, state.block_size, state.num_blocks);
        for (size_t i = 0; i < state.num_blocks; ++i) {
            decode_mapped_labels(input, state.num_mapped_dims, address);
            auto block_cells = builder->add_subspace(address);
            decode_cells(input, state.block_size, block_cells);
        }
        return builder->build(std::move(builder));
    }
};

} // namespace <unnamed>

void new_encode(const Value &value, nbostream &output) {
    TypeMeta meta(value.type());
    Format format(meta);
    output.putInt1_4Bytes(format.tag);
    encode_type(output, format, value.type(), meta);
    maybe_encode_num_blocks(output, meta, value.cells().size / meta.block_size);
    std::vector<vespalib::stringref> address(meta.mapped.size());
    std::vector<vespalib::stringref*> a_refs(meta.mapped.size());
    for (size_t i = 0; i < meta.mapped.size(); ++i) {
        a_refs[i] = &address[i];
    }
    auto view = value.index().create_view({});
    view->lookup({});
    size_t subspace;
    while (view->next_result(a_refs, subspace)) {
        encode_mapped_labels(output, meta.mapped.size(), address);
        if (meta.cell_type == CellType::FLOAT) {
            auto iter = value.cells().typify<float>().begin();
            iter += (subspace * meta.block_size);
            for (size_t i = 0; i < meta.block_size; ++i) {
                output << (float) *iter++;
            }
        } else {
            auto iter = value.cells().typify<double>().begin();
            iter += (subspace * meta.block_size);
            for (size_t i = 0; i < meta.block_size; ++i) {
                output << *iter++;
            }
        }
    }
}

std::unique_ptr<Value> new_decode(nbostream &input, const ValueBuilderFactory &factory) {
    Format format(input.getInt1_4Bytes());
    ValueType type = decode_type(input, format);
    TypeMeta meta(type);
    const size_t num_blocks = maybe_decode_num_blocks(input, meta, format);
    DecodeState state{type, meta.block_size, num_blocks, meta.mapped.size()};
    return typify_invoke<1,TypifyCellType,ContentDecoder>(meta.cell_type, input, state, factory);
}

} // namespace
<|endoftext|>


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

Program:   Medical Imaging & Interaction Toolkit
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html 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 "mitkParRecFileReader.h"
#include <itkImageFileReader.h>

#ifdef __GNUC__
#define stricmp strcasecmp
#endif

void mitk::ParRecFileReader::GenerateOutputInformation()
{
  mitk::Image::Pointer output = this->GetOutput();

  if ((output->IsInitialized()) && (this->GetMTime() <= m_ReadHeaderTime.GetMTime()))
    return;

  itkDebugMacro(<<"Reading PAR file for GenerateOutputInformation()" << m_FileName);

  // Check to see if we can read the file given the name or prefix
  //
  if ( m_FileName == "" && m_FilePrefix == "" )
  {
    throw itk::ImageFileReaderException(__FILE__, __LINE__, "One of FileName or FilePrefix must be non-empty");
  }

  m_RecFileName = "";
  if( m_FileName != "")
  {
    int extPos=m_FileName.find_last_of(".");
    if(extPos>=-1)
    {
      const char *ext=m_FileName.c_str()+extPos+1;
      if(stricmp(ext,"par")==0)
        m_RecFileName = m_FileName.substr(0,extPos);
      else
        m_RecFileName = m_FileName;
    }
    else
        m_RecFileName = m_FileName;

    m_RecFileName.append(".rec");

    bool headerRead = false;

    mitk::PixelType type;
    unsigned int dimension=0;
    unsigned int dimensions[4]={0,0,1,1};
    float sliceThickness=0.0;
    float sliceGap=0.0;
    float sliceSpacing=0.0;
    mitk::Vector3D thickness; thickness.Fill(1.0);
    mitk::Vector3D gap; gap.Fill(0.0);
    mitk::Vector3D spacing;

    FILE *f;
    f=fopen(m_FileName.c_str(), "r");
    if(f!=NULL)
    {
      while(!feof(f))
      {
        char s[300], *p;
        char* ignored = fgets(s,200,f);
        ++ignored;
        if(strstr(s,"Max. number of cardiac phases"))
        {
          p=strchr(s,':')+1;
          dimensions[3]=atoi(p);
          if(dimensions[3]>1)
            dimension=4;
        }
        else
        if(strstr(s,"Max. number of slices/locations"))
        {
          p=strchr(s,':')+1;
          dimensions[2]=atoi(p);
          if(dimension==0)
          {
            if(dimensions[2]>1)
              dimension=3;
            else
              dimension=2;
          }
        }
        else
        if(strstr(s,"Image pixel size"))
        {
          p=strchr(s,':')+1;
          int bpe=atoi(p);
          if(bpe==8)
            type=typeid(mitkIpUInt1_t);
          else
            type=typeid(mitkIpUInt2_t);
        }
        else
        if(strstr(s,"Recon resolution"))
        {
          p=s+strcspn(s,"0123456789");
          sscanf(p,"%u %u", dimensions, dimensions+1);
        }
        else
        if(strstr(s,"FOV (ap,fh,rl) [mm]"))
        {
          p=s+strcspn(s,"0123456789");
          sscanf(p,"%f %f %f", &thickness[0], &thickness[1], &thickness[2]);
        }
        else
        if(strstr(s,"Slice thickness [mm]"))
        {
          p=s+strcspn(s,"0123456789");
          sscanf(p,"%f", &sliceThickness);
        }
        else
        if(strstr(s,"Slice gap [mm]"))
        {
          p=s+strcspn(s,"-0123456789");
          sscanf(p,"%f", &sliceGap);
        }
      }
      fclose(f);

//C:\home\ivo\data\coronaries\ucsf-wholeheart-2.par
      sliceSpacing = sliceThickness+sliceGap;
      if((dimension>0) && (dimensions[0]>0) && (dimensions[1]>0) && (sliceThickness>0) && (sliceSpacing>0))
      {
        headerRead = true;
        if(fabs(thickness[0]/dimensions[2]-sliceSpacing)<0.0001)
          thickness[0]=thickness[1];
        else
        if(fabs(thickness[1]/dimensions[2]-sliceSpacing)<0.0001)
          thickness[1]=thickness[0];
        thickness[2]=sliceSpacing;

        thickness[0]/=dimensions[0];
        thickness[1]/=dimensions[1];
        spacing=thickness+gap;
      }
    }

    if( headerRead == false)
    {
      itk::ImageFileReaderException e(__FILE__, __LINE__);
      itk::OStringStream msg;
      msg << " Could not read file "
        << m_FileName.c_str();
      e.SetDescription(msg.str().c_str());
      throw e;
      return;
    }

    output->Initialize(type, dimension, dimensions);
    output->GetSlicedGeometry()->SetSpacing(spacing);

    //output->GetSlicedGeometry()->SetGeometry2D(mitk::Image::BuildStandardPlaneGeometry2D(output->GetSlicedGeometry(), dimensions).GetPointer(), 0);
    output->GetSlicedGeometry()->SetEvenlySpaced();
  }

  m_ReadHeaderTime.Modified();
}

void mitk::ParRecFileReader::GenerateData()
{
  mitk::Image::Pointer output = this->GetOutput();

  // Check to see if we can read the file given the name or prefix
  //
  if ( m_RecFileName == "" )
  {
    throw itk::ImageFileReaderException(__FILE__, __LINE__, "FileName for rec-file empty");
  }

  if( m_RecFileName != "")
  {
    FILE *f = fopen(m_RecFileName.c_str(), "r");
    if(f==NULL)
    {
      throw itk::ImageFileReaderException(__FILE__, __LINE__, "Could not open rec-file.");
    }

    int zstart, zmax;
    int tstart, tmax;

    zstart=output->GetRequestedRegion().GetIndex(2);
    tstart=output->GetRequestedRegion().GetIndex(3);

    zmax=zstart+output->GetRequestedRegion().GetSize(2);
    tmax=tstart+output->GetRequestedRegion().GetSize(3);

    int sliceSize=output->GetDimension(0)*output->GetDimension(1)*output->GetPixelType().GetBpe()/8;
    void *data = malloc(sliceSize);

    bool ignore4Dtopogram=false;
    {
      int slicePlusTimeSize=output->GetDimension(0)*output->GetDimension(1)*output->GetDimension(3)*output->GetPixelType().GetBpe()/8;
    if(output->GetDimension(3)>1)
      ignore4Dtopogram=true;

      int z,t;
      for(t=tstart;t<tmax;++t)
        for(z=zstart;z<zmax;++z)
        {
          if(ignore4Dtopogram)
            fseek(f,slicePlusTimeSize*z+(sliceSize+1)*t,SEEK_SET);
          else
            fseek(f,slicePlusTimeSize*z+sliceSize*t,SEEK_SET);
          size_t ignored = fread(data, sliceSize, 1, f);
          ++ignored;
          output->SetSlice(data,z,t,0);
        }
    }
    //else
    //{
    //  for(;z<zmax;++z)
    //  {
    //    fseek(f,sliceSize*z,SEEK_SET);
    //    fread(data, sliceSize, 1, f);
    //    output->SetSlice(data,z,0,0);
    //  }
    //}
    free(data);

    fclose(f);
  }
}

bool mitk::ParRecFileReader::CanReadFile(const std::string filename, const std::string /*filePrefix*/, const std::string /*filePattern*/)
{
  // First check the extension
  if(  filename == "" )
    {
      //MITK_INFO<<"No filename specified."<<std::endl;
    return false;
    }

  bool extensionFound = false;
  std::string::size_type PARPos = filename.rfind(".par");
  if ((PARPos != std::string::npos)
      && (PARPos == filename.length() - 4))
    {
    extensionFound = true;
    }

  PARPos = filename.rfind(".PAR");
  if ((PARPos != std::string::npos)
      && (PARPos == filename.length() - 4))
    {
    extensionFound = true;
    }

  if( !extensionFound )
    {
      //MITK_INFO<<"The filename extension is not recognized."<<std::endl;
    return false;
    }

  return true;
}

mitk::ParRecFileReader::ParRecFileReader()
: m_FileName(""), m_FilePrefix(""), m_FilePattern("")
{
}

mitk::ParRecFileReader::~ParRecFileReader()
{
}
<commit_msg>Made ParRecFileReader locale independent.<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html 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 "mitkParRecFileReader.h"
#include <itkImageFileReader.h>

#ifdef __GNUC__
#define stricmp strcasecmp
#endif

void mitk::ParRecFileReader::GenerateOutputInformation()
{
  mitk::Image::Pointer output = this->GetOutput();

  if ((output->IsInitialized()) && (this->GetMTime() <= m_ReadHeaderTime.GetMTime()))
    return;

  itkDebugMacro(<<"Reading PAR file for GenerateOutputInformation()" << m_FileName);

  // Check to see if we can read the file given the name or prefix
  //
  if ( m_FileName == "" && m_FilePrefix == "" )
  {
    throw itk::ImageFileReaderException(__FILE__, __LINE__, "One of FileName or FilePrefix must be non-empty");
  }

  m_RecFileName = "";
  if( m_FileName != "")
  {
    int extPos=m_FileName.find_last_of(".");
    if(extPos>=-1)
    {
      const char *ext=m_FileName.c_str()+extPos+1;
      if(stricmp(ext,"par")==0)
        m_RecFileName = m_FileName.substr(0,extPos);
      else
        m_RecFileName = m_FileName;
    }
    else
        m_RecFileName = m_FileName;

    m_RecFileName.append(".rec");

    bool headerRead = false;

    mitk::PixelType type;
    unsigned int dimension=0;
    unsigned int dimensions[4]={0,0,1,1};
    float sliceThickness=0.0;
    float sliceGap=0.0;
    float sliceSpacing=0.0;
    mitk::Vector3D thickness; thickness.Fill(1.0);
    mitk::Vector3D gap; gap.Fill(0.0);
    mitk::Vector3D spacing;

    FILE *f;
    f=fopen(m_FileName.c_str(), "r");
    if(f!=NULL)
    {
      while(!feof(f))
      {
        char s[300], *p;
        char* ignored = fgets(s,200,f);
        ++ignored;
        if(strstr(s,"Max. number of cardiac phases"))
        {
          p=strchr(s,':')+1;
          dimensions[3]=atoi(p);
          if(dimensions[3]>1)
            dimension=4;
        }
        else
        if(strstr(s,"Max. number of slices/locations"))
        {
          p=strchr(s,':')+1;
          dimensions[2]=atoi(p);
          if(dimension==0)
          {
            if(dimensions[2]>1)
              dimension=3;
            else
              dimension=2;
          }
        }
        else
        if(strstr(s,"Image pixel size"))
        {
          p=strchr(s,':')+1;
          int bpe=atoi(p);
          if(bpe==8)
            type=typeid(mitkIpUInt1_t);
          else
            type=typeid(mitkIpUInt2_t);
        }
        else
        if(strstr(s,"Recon resolution"))
        {
          p=s+strcspn(s,"0123456789");
          sscanf(p,"%u %u", dimensions, dimensions+1);
        }
        else
        if(strstr(s,"FOV (ap,fh,rl) [mm]"))
        {
          p=s+strcspn(s,"0123456789");
          char *oldLocale = setlocale(LC_ALL, 0);
          sscanf(p,"%f %f %f", &thickness[0], &thickness[1], &thickness[2]);
          setlocale(LC_ALL, oldLocale);
        }
        else
        if(strstr(s,"Slice thickness [mm]"))
        {
          p=s+strcspn(s,"0123456789");
          char *oldLocale = setlocale(LC_ALL, 0);
          sscanf(p,"%f", &sliceThickness);
          setlocale(LC_ALL, oldLocale);
        }
        else
        if(strstr(s,"Slice gap [mm]"))
        {
          p=s+strcspn(s,"-0123456789");
          char *oldLocale = setlocale(LC_ALL, 0);
          sscanf(p,"%f", &sliceGap);
          setlocale(LC_ALL, oldLocale);
        }
      }
      fclose(f);

//C:\home\ivo\data\coronaries\ucsf-wholeheart-2.par
      sliceSpacing = sliceThickness+sliceGap;
      if((dimension>0) && (dimensions[0]>0) && (dimensions[1]>0) && (sliceThickness>0) && (sliceSpacing>0))
      {
        headerRead = true;
        if(fabs(thickness[0]/dimensions[2]-sliceSpacing)<0.0001)
          thickness[0]=thickness[1];
        else
        if(fabs(thickness[1]/dimensions[2]-sliceSpacing)<0.0001)
          thickness[1]=thickness[0];
        thickness[2]=sliceSpacing;

        thickness[0]/=dimensions[0];
        thickness[1]/=dimensions[1];
        spacing=thickness+gap;
      }
    }

    if( headerRead == false)
    {
      itk::ImageFileReaderException e(__FILE__, __LINE__);
      itk::OStringStream msg;
      msg << " Could not read file "
        << m_FileName.c_str();
      e.SetDescription(msg.str().c_str());
      throw e;
      return;
    }

    output->Initialize(type, dimension, dimensions);
    output->GetSlicedGeometry()->SetSpacing(spacing);

    //output->GetSlicedGeometry()->SetGeometry2D(mitk::Image::BuildStandardPlaneGeometry2D(output->GetSlicedGeometry(), dimensions).GetPointer(), 0);
    output->GetSlicedGeometry()->SetEvenlySpaced();
  }

  m_ReadHeaderTime.Modified();
}

void mitk::ParRecFileReader::GenerateData()
{
  mitk::Image::Pointer output = this->GetOutput();

  // Check to see if we can read the file given the name or prefix
  //
  if ( m_RecFileName == "" )
  {
    throw itk::ImageFileReaderException(__FILE__, __LINE__, "FileName for rec-file empty");
  }

  if( m_RecFileName != "")
  {
    FILE *f = fopen(m_RecFileName.c_str(), "r");
    if(f==NULL)
    {
      throw itk::ImageFileReaderException(__FILE__, __LINE__, "Could not open rec-file.");
    }

    int zstart, zmax;
    int tstart, tmax;

    zstart=output->GetRequestedRegion().GetIndex(2);
    tstart=output->GetRequestedRegion().GetIndex(3);

    zmax=zstart+output->GetRequestedRegion().GetSize(2);
    tmax=tstart+output->GetRequestedRegion().GetSize(3);

    int sliceSize=output->GetDimension(0)*output->GetDimension(1)*output->GetPixelType().GetBpe()/8;
    void *data = malloc(sliceSize);

    bool ignore4Dtopogram=false;
    {
      int slicePlusTimeSize=output->GetDimension(0)*output->GetDimension(1)*output->GetDimension(3)*output->GetPixelType().GetBpe()/8;
    if(output->GetDimension(3)>1)
      ignore4Dtopogram=true;

      int z,t;
      for(t=tstart;t<tmax;++t)
        for(z=zstart;z<zmax;++z)
        {
          if(ignore4Dtopogram)
            fseek(f,slicePlusTimeSize*z+(sliceSize+1)*t,SEEK_SET);
          else
            fseek(f,slicePlusTimeSize*z+sliceSize*t,SEEK_SET);
          size_t ignored = fread(data, sliceSize, 1, f);
          ++ignored;
          output->SetSlice(data,z,t,0);
        }
    }
    //else
    //{
    //  for(;z<zmax;++z)
    //  {
    //    fseek(f,sliceSize*z,SEEK_SET);
    //    fread(data, sliceSize, 1, f);
    //    output->SetSlice(data,z,0,0);
    //  }
    //}
    free(data);

    fclose(f);
  }
}

bool mitk::ParRecFileReader::CanReadFile(const std::string filename, const std::string /*filePrefix*/, const std::string /*filePattern*/)
{
  // First check the extension
  if(  filename == "" )
    {
      //MITK_INFO<<"No filename specified."<<std::endl;
    return false;
    }

  bool extensionFound = false;
  std::string::size_type PARPos = filename.rfind(".par");
  if ((PARPos != std::string::npos)
      && (PARPos == filename.length() - 4))
    {
    extensionFound = true;
    }

  PARPos = filename.rfind(".PAR");
  if ((PARPos != std::string::npos)
      && (PARPos == filename.length() - 4))
    {
    extensionFound = true;
    }

  if( !extensionFound )
    {
      //MITK_INFO<<"The filename extension is not recognized."<<std::endl;
    return false;
    }

  return true;
}

mitk::ParRecFileReader::ParRecFileReader()
: m_FileName(""), m_FilePrefix(""), m_FilePattern("")
{
}

mitk::ParRecFileReader::~ParRecFileReader()
{
}
<|endoftext|>


<commit_before>
/**
 * @file ComputeCombination.hpp
 * This is the base class to ease computing combination of data for GNSS data structures.
 */

#ifndef Compute_Combination_GPSTK
#define Compute_Combination_GPSTK

//============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk 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
//  any later version.
//
//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Dagoberto Salazar - gAGE. 2007
//
//============================================================================

#include "DataStructures.hpp"

namespace gpstk
{

    /** @addtogroup DataStructures */
    //@{

    /// This class eases computing combination of data for GNSS data structures.
    class ComputeCombination
    {
    public:

        /// Default constructor
        ComputeCombination() {};

        /** Returns a satTypeValueMap object, adding the new data generated when calling this object.
         *
         * @param gData     Data object holding the data.
         */
        virtual satTypeValueMap& Combine(satTypeValueMap& gData)
        {
            double value1(0.0);
            double value2(0.0);

            SatIDSet satRejectedSet;

            // Loop through all the satellites
            satTypeValueMap::iterator it;
            for (it = gData.begin(); it != gData.end(); ++it)
            {
                try
                {
                    // Try to extract the values
                    value1 = (*it).second(type1);
                    value2 = (*it).second(type2);
                }
                catch(...)
                {
                    // If some value is missing, then schedule this satellite for removal
                    satRejectedSet.insert( (*it).first );
                    continue;
                }
                // If everything is OK, then get the new value inside the structure
                (*it).second[resultType] = getCombination(value1, value2);
            }
            // Remove satellites with missing data
            gData.removeSatID(satRejectedSet);

            return gData;
        };

        /** Returns a gnnsSatTypeValue object, adding the new data generated when calling this object.
         *
         * @param gData    Data object holding the data.
         */
        virtual gnssSatTypeValue& Combine(gnssSatTypeValue& gData)
        {
            (*this).Combine(gData.body);
            return gData;
        };

        /** Returns a gnnsRinex object, adding the new data generated when calling this object.
         *
         * @param gData    Data object holding the data.
         */
        virtual gnssRinex& Combine(gnssRinex& gData)
        {
            (*this).Combine(gData.body);
            return gData;
        };

        /// Destructor
        virtual ~ComputeCombination() {};

    protected:
        /// Compute the combination of observables. You must define this method according to your specific combination.
        virtual double getCombination(const double& obs1, const double& obs2) = 0;

        TypeID type1;
        TypeID type2;
        TypeID resultType;

   }; // end class ComputeCombination

    /// Input operator from gnssSatTypeValue to ComputeCombination.
    inline gnssSatTypeValue& operator>>(gnssSatTypeValue& gData, ComputeCombination& compComb)
    {
            compComb.Combine(gData);
            return gData;
    }

    /// Input operator from gnssRinex to ComputeCombination.
    inline gnssRinex& operator>>(gnssRinex& gData, ComputeCombination& compComb)
    {
            compComb.Combine(gData);
            return gData;
    }

   //@}

}

#endif
<commit_msg>Changes in documentation.<commit_after>
/**
 * @file ComputeCombination.hpp
 * This is the base class to ease computing combination of data for GNSS data structures.
 */

#ifndef Compute_Combination_GPSTK
#define Compute_Combination_GPSTK

//============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk 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
//  any later version.
//
//  The GPSTk 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 GPSTk; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Dagoberto Salazar - gAGE. 2007
//
//============================================================================

#include "DataStructures.hpp"

namespace gpstk
{

    /** @addtogroup DataStructures */
    //@{

    /// This class eases computing combination of data for GNSS data structures.
    class ComputeCombination
    {
    public:

        /// Default constructor
        ComputeCombination() {};

        /** Returns a satTypeValueMap object, adding the new data generated when calling this object.
         *
         * @param gData     Data object holding the data.
         */
        virtual satTypeValueMap& Combine(satTypeValueMap& gData)
        {
            double value1(0.0);
            double value2(0.0);

            SatIDSet satRejectedSet;

            // Loop through all the satellites
            satTypeValueMap::iterator it;
            for (it = gData.begin(); it != gData.end(); ++it)
            {
                try
                {
                    // Try to extract the values
                    value1 = (*it).second(type1);
                    value2 = (*it).second(type2);
                }
                catch(...)
                {
                    // If some value is missing, then schedule this satellite for removal
                    satRejectedSet.insert( (*it).first );
                    continue;
                }
                // If everything is OK, then get the new value inside the structure
                (*it).second[resultType] = getCombination(value1, value2);
            }
            // Remove satellites with missing data
            gData.removeSatID(satRejectedSet);

            return gData;
        };

        /** Returns a gnnsSatTypeValue object, adding the new data generated when calling this object.
         *
         * @param gData    Data object holding the data.
         */
        virtual gnssSatTypeValue& Combine(gnssSatTypeValue& gData)
        {
            (*this).Combine(gData.body);
            return gData;
        };

        /** Returns a gnnsRinex object, adding the new data generated when calling this object.
         *
         * @param gData    Data object holding the data.
         */
        virtual gnssRinex& Combine(gnssRinex& gData)
        {
            (*this).Combine(gData.body);
            return gData;
        };

        /// Destructor
        virtual ~ComputeCombination() {};

    protected:
        /// Compute the combination of observables. You must define this method according to your specific combination.
        virtual double getCombination(const double& obs1, const double& obs2) = 0;

        /// Type of observation to be combined. Nro 1.
        TypeID type1;

        /// Type of observation to be combined. Nro 1.
        TypeID type2;

        /// Type assigned to the resulting combination.
        TypeID resultType;

   }; // end class ComputeCombination

    /// Input operator from gnssSatTypeValue to ComputeCombination.
    inline gnssSatTypeValue& operator>>(gnssSatTypeValue& gData, ComputeCombination& compComb)
    {
            compComb.Combine(gData);
            return gData;
    }

    /// Input operator from gnssRinex to ComputeCombination.
    inline gnssRinex& operator>>(gnssRinex& gData, ComputeCombination& compComb)
    {
            compComb.Combine(gData);
            return gData;
    }

   //@}

}

#endif
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2018 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "PerformanceCounterSelection.h"
#include <QMenu>
#include <QSet>
#include "Code/Interface/QRDInterface.h"
#include "Code/Resources.h"
#include "ui_PerformanceCounterSelection.h"

#include <unordered_map>

#define JSON_ID "rdocPerformanceCounterSettings"
#define JSON_VER 1

// we can't specialise the template, but creating an overload works. This lets us use
// QSet<GPUCounter>
inline uint qHash(const GPUCounter &t)
{
  return qHash(uint32_t(t));
}

namespace
{
enum class CounterFamily
{
  Unknown,
  Generic,
  AMD,
  Intel,
  NVIDIA,
};

CounterFamily GetCounterFamily(GPUCounter counter)
{
  if(IsAMDCounter(counter))
  {
    return CounterFamily::AMD;
  }
  else if(IsIntelCounter(counter))
  {
    return CounterFamily::Intel;
  }
  else if(IsNvidiaCounter(counter))
  {
    return CounterFamily::NVIDIA;
  }

  return CounterFamily::Generic;
}

QString ToString(CounterFamily family)
{
  switch(family)
  {
    case CounterFamily::AMD: return lit("AMD");
    case CounterFamily::Generic: return lit("Generic");
    case CounterFamily::Intel: return lit("Intel");
    case CounterFamily::NVIDIA: return lit("NVIDIA");
    case CounterFamily::Unknown: return lit("Unknown");
  }

  return QString();
}
}

const int PerformanceCounterSelection::CounterDescriptionRole = Qt::UserRole + 1;
const int PerformanceCounterSelection::CounterIdRole = Qt::UserRole + 2;
const int PerformanceCounterSelection::PreviousCheckStateRole = Qt::UserRole + 3;

void PerformanceCounterSelection::uncheckAllChildren(RDTreeWidgetItem *item)
{
  for(int i = 0; i < item->childCount(); i++)
  {
    uncheckAllChildren(item->child(i));

    item->child(i)->setCheckState(0, Qt::Unchecked);
    item->child(i)->setData(0, PreviousCheckStateRole, Qt::Unchecked);
  }
}

void PerformanceCounterSelection::checkAllChildren(RDTreeWidgetItem *item)
{
  for(int i = 0; i < item->childCount(); i++)
  {
    checkAllChildren(item->child(i));

    item->child(i)->setCheckState(0, Qt::Checked);
    item->child(i)->setData(0, PreviousCheckStateRole, Qt::Checked);
  }
}

void PerformanceCounterSelection::updateParentCheckState(RDTreeWidgetItem *item)
{
  if(!item)
    return;

  int numChecked = 0;
  int numPartial = 0;

  for(int i = 0; i < item->childCount(); i++)
  {
    Qt::CheckState state = item->child(i)->checkState(0);
    if(state == Qt::PartiallyChecked)
      numPartial++;
    else if(state == Qt::Checked)
      numChecked++;
  }

  if(numChecked == item->childCount())
    item->setCheckState(0, Qt::Checked);
  else if(numChecked > 0 || numPartial > 0)
    item->setCheckState(0, Qt::PartiallyChecked);
  else
    item->setCheckState(0, Qt::Unchecked);

  item->setData(0, PreviousCheckStateRole, item->checkState(0));

  updateParentCheckState(item->parent());
}

void PerformanceCounterSelection::expandToNode(RDTreeWidgetItem *node)
{
  RDTreeWidgetItem *n = node;
  while(node != NULL)
  {
    ui->counterTree->expandItem(node);
    node = node->parent();
  }

  if(n)
    ui->counterTree->scrollToItem(n);
}

PerformanceCounterSelection::PerformanceCounterSelection(ICaptureContext &ctx,
                                                         const QList<GPUCounter> &selectedCounters,
                                                         QWidget *parent)
    : QDialog(parent), ui(new Ui::PerformanceCounterSelection), m_Ctx(ctx)
{
  ui->setupUi(this);

  setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

  ui->counterTree->setColumns({QString()});
  ui->counterTree->setHeaderHidden(true);

  connect(ui->counterTree, &RDTreeWidget::currentItemChanged,
          [this](RDTreeWidgetItem *item, RDTreeWidgetItem *) -> void {
            const QVariant d = item->data(0, CounterDescriptionRole);

            if(d.isValid())
            {
              ui->counterDescription->setText(
                  QString(lit("%1
%2")).arg(item->text(0)).arg(d.toString()));
            }
          });

  connect(ui->save, &QPushButton::pressed, this, &PerformanceCounterSelection::Save);
  connect(ui->load, &QPushButton::pressed, this, &PerformanceCounterSelection::Load);
  connect(ui->sampleCounters, &QPushButton::pressed, this, &PerformanceCounterSelection::accept);
  connect(ui->cancel, &QPushButton::pressed, this, &PerformanceCounterSelection::reject);

  connect(ui->counterTree, &RDTreeWidget::itemChanged, [this](RDTreeWidgetItem *item, int) -> void {
    const QVariant d = item->data(0, CounterIdRole);

    static bool recurse = false;

    if(d.isValid())
    {
      if(item->checkState(0) == Qt::Checked)
      {
        // Add
        QListWidgetItem *listItem = new QListWidgetItem(ui->enabledCounters);
        listItem->setText(item->text(0));
        listItem->setData(CounterIdRole, d);
        m_SelectedCounters.insert((GPUCounter)d.toUInt(), listItem);
      }
      else
      {
        // Remove
        QListWidgetItem *listItem = m_SelectedCounters.take((GPUCounter)d.toUInt());
        delete listItem;
      }

      if(!recurse)
      {
        recurse = true;
        updateParentCheckState(item->parent());
        recurse = false;
      }
    }
    else if(!recurse)
    {
      Qt::CheckState prev = item->data(0, PreviousCheckStateRole).value<Qt::CheckState>();

      if(item->checkState(0) != prev)
      {
        recurse = true;

        if(item->checkState(0) == Qt::Checked)
        {
          checkAllChildren(item);
        }
        else
        {
          uncheckAllChildren(item);
        }

        item->setData(0, PreviousCheckStateRole, item->checkState(0));

        updateParentCheckState(item);

        recurse = false;
      }
    }
  });

  ui->counterTree->setMouseTracking(true);

  ctx.Replay().AsyncInvoke([this, selectedCounters](IReplayController *controller) -> void {
    QVector<CounterDescription> counterDescriptions;
    for(const GPUCounter counter : controller->EnumerateCounters())
    {
      counterDescriptions.append(controller->DescribeCounter(counter));
    }

    GUIInvoke::call([counterDescriptions, selectedCounters, this]() -> void {
      SetCounters(counterDescriptions);
      SetSelectedCounters(selectedCounters);
    });
  });

  ui->counterTree->setContextMenuPolicy(Qt::CustomContextMenu);
  QObject::connect(ui->counterTree, &RDTreeWidget::customContextMenuRequested, this,
                   &PerformanceCounterSelection::counterTree_contextMenu);
}

PerformanceCounterSelection::~PerformanceCounterSelection()
{
  delete ui;
}

void PerformanceCounterSelection::SetCounters(const QVector<CounterDescription> &descriptions)
{
  ui->counterTree->clear();
  ui->enabledCounters->clear();

  RDTreeWidgetItem *currentRoot = NULL;
  CounterFamily currentFamily = CounterFamily::Unknown;

  std::unordered_map<std::string, RDTreeWidgetItem *> categories;

  for(const CounterDescription &desc : descriptions)
  {
    m_CounterToUuid[desc.counter] = desc.uuid;
    m_UuidToCounter[desc.uuid] = desc.counter;

    const CounterFamily family = GetCounterFamily(desc.counter);
    if(family != currentFamily)
    {
      currentRoot = new RDTreeWidgetItem();
      currentRoot->setText(0, ToString(family));
      currentRoot->setCheckState(0, Qt::Unchecked);
      currentRoot->setData(0, PreviousCheckStateRole, Qt::Unchecked);
      ui->counterTree->addTopLevelItem(currentRoot);

      categories.clear();

      currentFamily = family;
    }

    RDTreeWidgetItem *categoryItem = NULL;

    const std::string category = desc.category;
    auto categoryIterator = categories.find(category);

    if(categoryIterator == categories.end())
    {
      RDTreeWidgetItem *item = new RDTreeWidgetItem();
      item->setText(0, desc.category);
      item->setCheckState(0, Qt::Unchecked);
      item->setData(0, PreviousCheckStateRole, Qt::Unchecked);
      currentRoot->addChild(item);

      categories[category] = item;
      categoryItem = item;
    }
    else
    {
      categoryItem = categoryIterator->second;
    }

    RDTreeWidgetItem *counterItem = new RDTreeWidgetItem();
    counterItem->setText(0, desc.name);
    counterItem->setData(0, CounterDescriptionRole, desc.description);
    counterItem->setData(0, CounterIdRole, (uint32_t)desc.counter);
    counterItem->setCheckState(0, Qt::Unchecked);
    counterItem->setData(0, PreviousCheckStateRole, Qt::Unchecked);
    categoryItem->addChild(counterItem);

    m_CounterToTreeItem[desc.counter] = counterItem;
  }
}

QList<GPUCounter> PerformanceCounterSelection::GetSelectedCounters() const
{
  return m_SelectedCounters.keys();
}

void PerformanceCounterSelection::SetSelectedCounters(const QList<GPUCounter> &counters)
{
  // We we walk over the complete tree, and toggle everything so it
  // matches the settings
  RDTreeWidgetItemIterator it(ui->counterTree);
  while(*it)
  {
    const QVariant id = (*it)->data(0, CounterIdRole);
    if(id.isValid())
    {
      const GPUCounter counter = (GPUCounter)id.toUInt();

      (*it)->setCheckState(0, counters.contains(counter) ? Qt::Checked : Qt::Unchecked);
    }

    // The loop above will uncheck all unknown counters, and not crash if some counter is no
    // longer present, or unknown

    ++it;
  }
}

void PerformanceCounterSelection::Save()
{
  QString filename = RDDialog::getSaveFileName(this, tr("Save File"), QDir::homePath(),
                                               tr("Performance Counter Settings (*.json)"));

  if(filename.isEmpty())
    return;

  QVariantList counterIds;
  for(const GPUCounter v : m_SelectedCounters.keys())
  {
    const Uuid uuid = m_CounterToUuid[v];
    QVariantList e;

    for(const uint32_t b : uuid.words)
    {
      e.append(b);
    }

    counterIds.append(QVariant(e));
  }

  QVariantMap doc;
  doc[lit("counters")] = counterIds;

  QFile f(filename);
  if(f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
  {
    SaveToJSON(doc, f, JSON_ID, JSON_VER);
  }
  else
  {
    RDDialog::critical(this, tr("Error saving config"),
                       tr("Couldn't open path %1 for write.").arg(filename));
  }
}

void PerformanceCounterSelection::Load()
{
  QString filename = RDDialog::getOpenFileName(this, tr("Load file"), QDir::homePath(),
                                               tr("Performance Counter Settings (*.json)"));

  if(filename.isEmpty())
    return;

  QVariantMap doc;
  QFile f(filename);
  if(f.open(QIODevice::ReadOnly | QIODevice::Text))
  {
    LoadFromJSON(doc, f, JSON_ID, JSON_VER);

    QSet<GPUCounter> selectedCounters;

    QVariantList counters = doc[lit("counters")].toList();

    for(const QVariant &counter : counters)
    {
      QVariantList bytes = counter.toList();
      Uuid uuid;

      if(bytes.size() != 4)
      {
        qWarning() << "Counter ID doesn't count 4 words";
        continue;
      }

      for(int i = 0; i < 4; ++i)
      {
        uuid.words[i] = bytes[i].toUInt();
      }

      if(!m_UuidToCounter.contains(uuid))
        continue;

      selectedCounters.insert(m_UuidToCounter[uuid]);
    }

    SetSelectedCounters(selectedCounters.toList());
  }
  else
  {
    RDDialog::critical(this, tr("Error loading config"),
                       tr("Couldn't open path %1 for reading.").arg(filename));
  }
}
void PerformanceCounterSelection::counterTree_contextMenu(const QPoint &pos)
{
  RDTreeWidgetItem *item = ui->counterTree->itemAt(pos);

  QMenu contextMenu(this);

  QAction expandAll(tr("&Expand All"), this);
  QAction collapseAll(tr("&Collapse All"), this);

  contextMenu.addAction(&expandAll);
  contextMenu.addAction(&collapseAll);

  expandAll.setIcon(Icons::arrow_out());
  collapseAll.setIcon(Icons::arrow_in());

  expandAll.setEnabled(item && item->childCount() > 0);
  collapseAll.setEnabled(item && item->childCount() > 0);

  QObject::connect(&expandAll, &QAction::triggered,
                   [this, item]() { ui->counterTree->expandAllItems(item); });

  QObject::connect(&collapseAll, &QAction::triggered,
                   [this, item]() { ui->counterTree->collapseAllItems(item); });

  RDDialog::show(&contextMenu, ui->counterTree->viewport()->mapToGlobal(pos));
}

void PerformanceCounterSelection::on_enabledCounters_activated(const QModelIndex &index)
{
  QListWidgetItem *item = ui->enabledCounters->item(index.row());

  if(!item)
    return;

  QVariant counterID = item->data(CounterIdRole);

  // locate the item in the description tree
  auto it = m_CounterToTreeItem.find((GPUCounter)counterID.toUInt());

  if(it != m_CounterToTreeItem.end())
  {
    ui->counterTree->setCurrentItem(it.value());
    ui->counterTree->setSelectedItem(it.value());

    expandToNode(it.value());
  }
}
<commit_msg>Make sure counter selection stays consistent and without duplicate items<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2018 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "PerformanceCounterSelection.h"
#include <QMenu>
#include <QSet>
#include "Code/Interface/QRDInterface.h"
#include "Code/Resources.h"
#include "ui_PerformanceCounterSelection.h"

#include <unordered_map>

#define JSON_ID "rdocPerformanceCounterSettings"
#define JSON_VER 1

// we can't specialise the template, but creating an overload works. This lets us use
// QSet<GPUCounter>
inline uint qHash(const GPUCounter &t)
{
  return qHash(uint32_t(t));
}

namespace
{
enum class CounterFamily
{
  Unknown,
  Generic,
  AMD,
  Intel,
  NVIDIA,
};

CounterFamily GetCounterFamily(GPUCounter counter)
{
  if(IsAMDCounter(counter))
  {
    return CounterFamily::AMD;
  }
  else if(IsIntelCounter(counter))
  {
    return CounterFamily::Intel;
  }
  else if(IsNvidiaCounter(counter))
  {
    return CounterFamily::NVIDIA;
  }

  return CounterFamily::Generic;
}

QString ToString(CounterFamily family)
{
  switch(family)
  {
    case CounterFamily::AMD: return lit("AMD");
    case CounterFamily::Generic: return lit("Generic");
    case CounterFamily::Intel: return lit("Intel");
    case CounterFamily::NVIDIA: return lit("NVIDIA");
    case CounterFamily::Unknown: return lit("Unknown");
  }

  return QString();
}
}

const int PerformanceCounterSelection::CounterDescriptionRole = Qt::UserRole + 1;
const int PerformanceCounterSelection::CounterIdRole = Qt::UserRole + 2;
const int PerformanceCounterSelection::PreviousCheckStateRole = Qt::UserRole + 3;

void PerformanceCounterSelection::uncheckAllChildren(RDTreeWidgetItem *item)
{
  for(int i = 0; i < item->childCount(); i++)
  {
    uncheckAllChildren(item->child(i));

    item->child(i)->setCheckState(0, Qt::Unchecked);
    item->child(i)->setData(0, PreviousCheckStateRole, Qt::Unchecked);
  }
}

void PerformanceCounterSelection::checkAllChildren(RDTreeWidgetItem *item)
{
  for(int i = 0; i < item->childCount(); i++)
  {
    checkAllChildren(item->child(i));

    item->child(i)->setCheckState(0, Qt::Checked);
    item->child(i)->setData(0, PreviousCheckStateRole, Qt::Checked);
  }
}

void PerformanceCounterSelection::updateParentCheckState(RDTreeWidgetItem *item)
{
  if(!item)
    return;

  int numChecked = 0;
  int numPartial = 0;

  for(int i = 0; i < item->childCount(); i++)
  {
    Qt::CheckState state = item->child(i)->checkState(0);
    if(state == Qt::PartiallyChecked)
      numPartial++;
    else if(state == Qt::Checked)
      numChecked++;
  }

  if(numChecked == item->childCount())
    item->setCheckState(0, Qt::Checked);
  else if(numChecked > 0 || numPartial > 0)
    item->setCheckState(0, Qt::PartiallyChecked);
  else
    item->setCheckState(0, Qt::Unchecked);

  item->setData(0, PreviousCheckStateRole, item->checkState(0));

  updateParentCheckState(item->parent());
}

void PerformanceCounterSelection::expandToNode(RDTreeWidgetItem *node)
{
  RDTreeWidgetItem *n = node;
  while(node != NULL)
  {
    ui->counterTree->expandItem(node);
    node = node->parent();
  }

  if(n)
    ui->counterTree->scrollToItem(n);
}

PerformanceCounterSelection::PerformanceCounterSelection(ICaptureContext &ctx,
                                                         const QList<GPUCounter> &selectedCounters,
                                                         QWidget *parent)
    : QDialog(parent), ui(new Ui::PerformanceCounterSelection), m_Ctx(ctx)
{
  ui->setupUi(this);

  setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

  ui->counterTree->setColumns({QString()});
  ui->counterTree->setHeaderHidden(true);

  connect(ui->counterTree, &RDTreeWidget::currentItemChanged,
          [this](RDTreeWidgetItem *item, RDTreeWidgetItem *) -> void {
            const QVariant d = item->data(0, CounterDescriptionRole);

            if(d.isValid())
            {
              ui->counterDescription->setText(
                  QString(lit("%1
%2")).arg(item->text(0)).arg(d.toString()));
            }
          });

  connect(ui->save, &QPushButton::pressed, this, &PerformanceCounterSelection::Save);
  connect(ui->load, &QPushButton::pressed, this, &PerformanceCounterSelection::Load);
  connect(ui->sampleCounters, &QPushButton::pressed, this, &PerformanceCounterSelection::accept);
  connect(ui->cancel, &QPushButton::pressed, this, &PerformanceCounterSelection::reject);

  connect(ui->counterTree, &RDTreeWidget::itemChanged, [this](RDTreeWidgetItem *item, int) -> void {
    const QVariant d = item->data(0, CounterIdRole);

    static bool recurse = false;

    if(d.isValid())
    {
      if(item->checkState(0) == Qt::Checked)
      {
        // Add
        if(!m_SelectedCounters.contains((GPUCounter)d.toUInt()))
        {
          QListWidgetItem *listItem = new QListWidgetItem(ui->enabledCounters);
          listItem->setText(item->text(0));
          listItem->setData(CounterIdRole, d);
          m_SelectedCounters.insert((GPUCounter)d.toUInt(), listItem);
        }
      }
      else
      {
        // Remove
        QListWidgetItem *listItem = m_SelectedCounters.take((GPUCounter)d.toUInt());
        delete listItem;
      }

      if(!recurse)
      {
        recurse = true;
        updateParentCheckState(item->parent());
        recurse = false;
      }
    }
    else if(!recurse)
    {
      Qt::CheckState prev = item->data(0, PreviousCheckStateRole).value<Qt::CheckState>();

      if(item->checkState(0) != prev)
      {
        recurse = true;

        if(item->checkState(0) == Qt::Checked)
        {
          checkAllChildren(item);
        }
        else
        {
          uncheckAllChildren(item);
        }

        item->setData(0, PreviousCheckStateRole, item->checkState(0));

        updateParentCheckState(item);

        recurse = false;
      }
    }
  });

  ui->counterTree->setMouseTracking(true);

  ctx.Replay().AsyncInvoke([this, selectedCounters](IReplayController *controller) -> void {
    QVector<CounterDescription> counterDescriptions;
    for(const GPUCounter counter : controller->EnumerateCounters())
    {
      counterDescriptions.append(controller->DescribeCounter(counter));
    }

    GUIInvoke::call([counterDescriptions, selectedCounters, this]() -> void {
      SetCounters(counterDescriptions);
      SetSelectedCounters(selectedCounters);
    });
  });

  ui->counterTree->setContextMenuPolicy(Qt::CustomContextMenu);
  QObject::connect(ui->counterTree, &RDTreeWidget::customContextMenuRequested, this,
                   &PerformanceCounterSelection::counterTree_contextMenu);
}

PerformanceCounterSelection::~PerformanceCounterSelection()
{
  delete ui;
}

void PerformanceCounterSelection::SetCounters(const QVector<CounterDescription> &descriptions)
{
  ui->counterTree->clear();
  ui->enabledCounters->clear();

  RDTreeWidgetItem *currentRoot = NULL;
  CounterFamily currentFamily = CounterFamily::Unknown;

  std::unordered_map<std::string, RDTreeWidgetItem *> categories;

  for(const CounterDescription &desc : descriptions)
  {
    m_CounterToUuid[desc.counter] = desc.uuid;
    m_UuidToCounter[desc.uuid] = desc.counter;

    const CounterFamily family = GetCounterFamily(desc.counter);
    if(family != currentFamily)
    {
      currentRoot = new RDTreeWidgetItem();
      currentRoot->setText(0, ToString(family));
      currentRoot->setCheckState(0, Qt::Unchecked);
      currentRoot->setData(0, PreviousCheckStateRole, Qt::Unchecked);
      ui->counterTree->addTopLevelItem(currentRoot);

      categories.clear();

      currentFamily = family;
    }

    RDTreeWidgetItem *categoryItem = NULL;

    const std::string category = desc.category;
    auto categoryIterator = categories.find(category);

    if(categoryIterator == categories.end())
    {
      RDTreeWidgetItem *item = new RDTreeWidgetItem();
      item->setText(0, desc.category);
      item->setCheckState(0, Qt::Unchecked);
      item->setData(0, PreviousCheckStateRole, Qt::Unchecked);
      currentRoot->addChild(item);

      categories[category] = item;
      categoryItem = item;
    }
    else
    {
      categoryItem = categoryIterator->second;
    }

    RDTreeWidgetItem *counterItem = new RDTreeWidgetItem();
    counterItem->setText(0, desc.name);
    counterItem->setData(0, CounterDescriptionRole, desc.description);
    counterItem->setData(0, CounterIdRole, (uint32_t)desc.counter);
    counterItem->setCheckState(0, Qt::Unchecked);
    counterItem->setData(0, PreviousCheckStateRole, Qt::Unchecked);
    categoryItem->addChild(counterItem);

    m_CounterToTreeItem[desc.counter] = counterItem;
  }
}

QList<GPUCounter> PerformanceCounterSelection::GetSelectedCounters() const
{
  return m_SelectedCounters.keys();
}

void PerformanceCounterSelection::SetSelectedCounters(const QList<GPUCounter> &counters)
{
  // We we walk over the complete tree, and toggle everything so it
  // matches the settings
  RDTreeWidgetItemIterator it(ui->counterTree);
  while(*it)
  {
    const QVariant id = (*it)->data(0, CounterIdRole);
    if(id.isValid())
    {
      const GPUCounter counter = (GPUCounter)id.toUInt();

      (*it)->setCheckState(0, counters.contains(counter) ? Qt::Checked : Qt::Unchecked);
    }

    // The loop above will uncheck all unknown counters, and not crash if some counter is no
    // longer present, or unknown

    ++it;
  }
}

void PerformanceCounterSelection::Save()
{
  QString filename = RDDialog::getSaveFileName(this, tr("Save File"), QDir::homePath(),
                                               tr("Performance Counter Settings (*.json)"));

  if(filename.isEmpty())
    return;

  QVariantList counterIds;
  for(const GPUCounter v : m_SelectedCounters.keys())
  {
    const Uuid uuid = m_CounterToUuid[v];
    QVariantList e;

    for(const uint32_t b : uuid.words)
    {
      e.append(b);
    }

    counterIds.append(QVariant(e));
  }

  QVariantMap doc;
  doc[lit("counters")] = counterIds;

  QFile f(filename);
  if(f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
  {
    SaveToJSON(doc, f, JSON_ID, JSON_VER);
  }
  else
  {
    RDDialog::critical(this, tr("Error saving config"),
                       tr("Couldn't open path %1 for write.").arg(filename));
  }
}

void PerformanceCounterSelection::Load()
{
  QString filename = RDDialog::getOpenFileName(this, tr("Load file"), QDir::homePath(),
                                               tr("Performance Counter Settings (*.json)"));

  if(filename.isEmpty())
    return;

  QVariantMap doc;
  QFile f(filename);
  if(f.open(QIODevice::ReadOnly | QIODevice::Text))
  {
    LoadFromJSON(doc, f, JSON_ID, JSON_VER);

    QSet<GPUCounter> selectedCounters;

    QVariantList counters = doc[lit("counters")].toList();

    for(const QVariant &counter : counters)
    {
      QVariantList bytes = counter.toList();
      Uuid uuid;

      if(bytes.size() != 4)
      {
        qWarning() << "Counter ID doesn't count 4 words";
        continue;
      }

      for(int i = 0; i < 4; ++i)
      {
        uuid.words[i] = bytes[i].toUInt();
      }

      if(!m_UuidToCounter.contains(uuid))
        continue;

      selectedCounters.insert(m_UuidToCounter[uuid]);
    }

    SetSelectedCounters(selectedCounters.toList());
  }
  else
  {
    RDDialog::critical(this, tr("Error loading config"),
                       tr("Couldn't open path %1 for reading.").arg(filename));
  }
}
void PerformanceCounterSelection::counterTree_contextMenu(const QPoint &pos)
{
  RDTreeWidgetItem *item = ui->counterTree->itemAt(pos);

  QMenu contextMenu(this);

  QAction expandAll(tr("&Expand All"), this);
  QAction collapseAll(tr("&Collapse All"), this);

  contextMenu.addAction(&expandAll);
  contextMenu.addAction(&collapseAll);

  expandAll.setIcon(Icons::arrow_out());
  collapseAll.setIcon(Icons::arrow_in());

  expandAll.setEnabled(item && item->childCount() > 0);
  collapseAll.setEnabled(item && item->childCount() > 0);

  QObject::connect(&expandAll, &QAction::triggered,
                   [this, item]() { ui->counterTree->expandAllItems(item); });

  QObject::connect(&collapseAll, &QAction::triggered,
                   [this, item]() { ui->counterTree->collapseAllItems(item); });

  RDDialog::show(&contextMenu, ui->counterTree->viewport()->mapToGlobal(pos));
}

void PerformanceCounterSelection::on_enabledCounters_activated(const QModelIndex &index)
{
  QListWidgetItem *item = ui->enabledCounters->item(index.row());

  if(!item)
    return;

  QVariant counterID = item->data(CounterIdRole);

  // locate the item in the description tree
  auto it = m_CounterToTreeItem.find((GPUCounter)counterID.toUInt());

  if(it != m_CounterToTreeItem.end())
  {
    ui->counterTree->setCurrentItem(it.value());
    ui->counterTree->setSelectedItem(it.value());

    expandToNode(it.value());
  }
}
<|endoftext|>


<commit_before>/*
	Copyright 2021 Jordi SUBIRANA

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
	Software, and to permit persons to whom the Software is furnished to do so, subject
	to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
	PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
	CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
	OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "BasicRenderPipeline.hpp"

#include "Resources.hpp"

using namespace at;

namespace
{
	constexpr size_t targetThreadCount = 4;

	const size_t threadCount = std::min(targetThreadCount, TaskManager::getInstance().getSize());
}

BasicRenderPipeline::BasicRenderPipeline(const RenderPipeline::Settings& settings) :
	RenderPipeline(settings),
	m_maxFramesInFlight(settings.maxFramesInFlight),
	m_totalTime(0.0f)
{
	const auto windowSize = settings.window->getSize();

	//----- OBJECT RESOURCES -----//

	// Descriptor set layout
	{
		DescriptorSetLayout::Settings descriptorSetLayoutSettings;
		descriptorSetLayoutSettings.bindings =
		{
			{ DescriptorType::UniformBuffer, 0, 1, ShaderStage::Vertex },
			{ DescriptorType::CombinedImageSampler, 1, 1, ShaderStage::Fragment }
		};

		m_objectDescriptorSetLayout = DescriptorSetLayout::create(descriptorSetLayoutSettings);
	}

	// Descriptor pool
	{
		DescriptorPool::Settings descriptorPoolSettings;
		descriptorPoolSettings.layout = m_objectDescriptorSetLayout;
		descriptorPoolSettings.pageSize = m_maxFramesInFlight * objectCount;

		m_objectDescriptorPool = DescriptorPool::create(descriptorPoolSettings);
	}

	//----- FRAME RESOURCES -----//

	// Descriptor set layout
	{
		DescriptorSetLayout::Settings descriptorSetLayoutSettings;
		descriptorSetLayoutSettings.bindings =
		{
			{ DescriptorType::UniformBuffer, 0, 1, ShaderStage::Vertex }
		};

		m_frameDescriptorSetLayout = DescriptorSetLayout::create(descriptorSetLayoutSettings);
	}

	// Descriptor pool
	{
		DescriptorPool::Settings descriptorPoolSettings;
		descriptorPoolSettings.layout = m_frameDescriptorSetLayout;
		descriptorPoolSettings.pageSize = m_maxFramesInFlight;

		m_frameDescriptorPool = DescriptorPool::create(descriptorPoolSettings);
	}

	// Uniform buffers & descriptor sets
	m_frameUniformBuffers.reserve(m_maxFramesInFlight);
	m_frameDescriptorSets.reserve(m_maxFramesInFlight);

	for (uint32_t j = 0; j < m_maxFramesInFlight; j++)
	{
		auto uniformBuffer = Buffer::create({ BufferUsage::Uniform, sizeof(UniformFrameElement), true });

		m_frameUniformBuffers.push_back(uniformBuffer);

		auto descriptorSet = m_frameDescriptorPool->createSet();

		descriptorSet->update(0, uniformBuffer);

		m_frameDescriptorSets.push_back(descriptorSet);
	}

	//----- THREAD RESOURCES -----//
	auto& taskManager = TaskManager::getInstance();
	const auto coreCount = taskManager.getSize();

	m_threadCommandBuffers.resize(coreCount);

	for (auto& commandBuffers : m_threadCommandBuffers)
		commandBuffers.resize(m_maxFramesInFlight);

	m_threadCommandPools.reserve(coreCount);
	for (size_t i = 0; i < coreCount; i++)
		m_threadCommandPools.push_back(CommandPool::create({}));

	//----- PIPELINE RESOURCES -----//

	// Graphics pipeline
	GraphicsPipeline::Settings pipelineSettings;
	pipelineSettings.viewport.size.x = static_cast<float>(windowSize.x);
	pipelineSettings.viewport.size.y = static_cast<float>(windowSize.y);
	pipelineSettings.scissor.size = windowSize;
	pipelineSettings.vertexShader = Shader::create({ vertShaderPath });
	pipelineSettings.fragmentShader = Shader::create({ fragShaderPath });
	pipelineSettings.renderPass = getRenderPass();
	pipelineSettings.descriptorSetLayouts = { m_frameDescriptorSetLayout, m_objectDescriptorSetLayout };
	pipelineSettings.vertexInput.attributes =
	{
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RG32_SFLOAT }
	};
	pipelineSettings.vertexInput.inputs =
	{
		{ 0, 0 },
		{ 0, 1 },
		{ 0, 2 }
	};

	m_pipeline = GraphicsPipeline::create(pipelineSettings);

	loadScene();
}

BasicRenderPipeline::~BasicRenderPipeline()
{
	Renderer::getInstance().waitForIdle();

	// Thread resources
	m_threadCommandBuffers.clear();
	m_threadCommandPools.clear();

	// Frame resources
	m_frameDescriptorSetLayout.reset();
	m_frameDescriptorSets.clear();
	m_frameDescriptorPool.reset();
	m_frameUniformBuffers.clear();

	// Object resources
	m_objectDescriptorSetLayout.reset();
	m_objectDescriptorPool.reset();
	m_objectFrameData.clear();
	m_scene.reset();

	m_pipeline.reset();
}

void BasicRenderPipeline::resize(const Vector2u& size)
{
	m_pipeline.reset();

	GraphicsPipeline::Settings pipelineSettings;
	pipelineSettings.viewport.size.x = static_cast<float>(size.x);
	pipelineSettings.viewport.size.y = static_cast<float>(size.y);
	pipelineSettings.scissor.size = size;
	pipelineSettings.vertexShader = Shader::create({ rscPath / "Shaders/vert.spv" });
	pipelineSettings.fragmentShader = Shader::create({ rscPath / "Shaders/frag.spv" });
	pipelineSettings.renderPass = getRenderPass();
	pipelineSettings.descriptorSetLayouts = { m_frameDescriptorSetLayout, m_objectDescriptorSetLayout };
	pipelineSettings.vertexInput.attributes =
	{
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RG32_SFLOAT }
	};
	pipelineSettings.vertexInput.inputs =
	{
		{ 0, 0 },
		{ 0, 1 },
		{ 0, 2 }
	};

	m_pipeline = GraphicsPipeline::create(pipelineSettings);
}

void BasicRenderPipeline::updateFrame(at::TimeStep elapsedTime)
{
	ATEMA_BENCHMARK("Scene::updateObjects")

	m_totalTime += elapsedTime.getSeconds();

	m_scene->updateObjects(elapsedTime, threadCount);
}

void BasicRenderPipeline::setupFrame(uint32_t frameIndex, Ptr<CommandBuffer> commandBuffer)
{
	updateUniformBuffers(frameIndex);

	auto& globalDescriptorSet = m_frameDescriptorSets[frameIndex];

	beginRenderPass(true);

	// Update objects buffers
	{
		ATEMA_BENCHMARK("CommandBuffers fill");

		// Clear previous command buffers
		for (auto& threadCommandBuffers : m_threadCommandBuffers)
		{
			threadCommandBuffers[frameIndex].clear();
		}

		auto& objects = m_scene->getObjects();

		auto& taskManager = TaskManager::getInstance();

		std::vector<Ptr<Task>> tasks;
		tasks.reserve(threadCount);

		std::vector<Ptr<CommandBuffer>> commandBuffers;
		commandBuffers.resize(threadCount);

		size_t firstIndex = 0;
		size_t size = objects.size() / threadCount;

		for (size_t taskIndex = 0; taskIndex < threadCount; taskIndex++)
		{
			auto lastIndex = firstIndex + size;

			if (taskIndex == threadCount - 1)
			{
				const auto remainingSize = objects.size() - lastIndex;

				lastIndex += remainingSize;
			}

			auto task = taskManager.createTask([this, taskIndex, firstIndex, lastIndex, &objects, &commandBuffers, globalDescriptorSet, frameIndex](size_t threadIndex)
				{
					auto& commandPool = m_threadCommandPools[threadIndex];
					auto commandBuffer = CommandBuffer::create({ commandPool, true, true });

					commandBuffer->beginSecondary(getRenderPass(), getCurrentFramebuffer());

					commandBuffer->bindPipeline(m_pipeline);

					for (size_t i = firstIndex; i < lastIndex; i++)
					{
						auto& object = objects[i];
						auto descriptorSet = m_objectFrameData[i].getDescriptorSet(frameIndex);

						commandBuffer->bindVertexBuffer(object.vertexBuffer, 0);

						commandBuffer->bindIndexBuffer(object.indexBuffer, IndexType::U32);

						commandBuffer->bindDescriptorSets({ globalDescriptorSet, descriptorSet });

						commandBuffer->drawIndexed(object.indexCount);
					}

					commandBuffer->end();

					m_threadCommandBuffers[threadIndex][frameIndex].push_back(commandBuffer);

					commandBuffers[taskIndex] = commandBuffer;
				});

			tasks.push_back(task);

			firstIndex += size;
		}

		for (auto& task : tasks)
			task->wait();

		commandBuffer->executeSecondaryCommands(commandBuffers);
	}

	endRenderPass();
}

void BasicRenderPipeline::loadScene()
{
	ATEMA_BENCHMARK("BasicRenderPipeline::loadScene")

	auto& commandPool = getCommandPools()[0];

	m_scene = std::make_shared<Scene>(commandPool);

	auto& objects = m_scene->getObjects();

	m_objectFrameData.reserve(objects.size());

	for (auto& object : objects)
	{
		ObjectFrameData objectFrameData(object, m_maxFramesInFlight, m_objectDescriptorPool);

		m_objectFrameData.push_back(objectFrameData);
	}
}

void BasicRenderPipeline::updateUniformBuffers(uint32_t frameIndex)
{
	ATEMA_BENCHMARK("BasicRenderPipeline::updateUniformBuffers")

		// Update global buffers
	{
		const auto windowSize = getWindow()->getSize();

		const auto angle = m_totalTime * zoomSpeed;

		const auto sin = std::sin(angle);
		const auto sinSlow = std::sin(angle / 2.0f + 3.14159f);

		const auto sign = (sin + 1.0f) / 2.0f;
		const auto signSlow = (sinSlow + 1.0f) / 2.0f;

		auto radius = modelScale * objectRow;
		radius = sign * radius + (1.0f - sign) * zoomOffset;
		//radius = signSlow * radius + (1.0f - signSlow) * zoomOffset;

		const auto pos = toCartesian({ radius, angle / 3.0f });

		auto z = modelScale * objectRow;
		z = signSlow * radius + (1.0f - signSlow) * zoomOffset;
		//z = zoomOffset;

		const Vector3f cameraPos(pos.x, pos.y, z);
		const Vector3f cameraTarget(0.0f, 0.0f, zoomOffset / 2.0f);
		const Vector3f cameraUp(0.0f, 0.0f, 1.0f);

		UniformFrameElement frameTransforms;
		frameTransforms.view = lookAt(cameraPos, cameraTarget, cameraUp);
		frameTransforms.proj = perspective(toRadians(45.0f), static_cast<float>(windowSize.x) / static_cast<float>(windowSize.y), 0.1f, 1000.0f);
		frameTransforms.proj[1][1] *= -1;

		auto buffer = m_frameUniformBuffers[frameIndex];

		void* data = buffer->map();

		memcpy(data, static_cast<void*>(&frameTransforms), sizeof(UniformFrameElement));

		buffer->unmap();
	}

	// Update objects buffers
	{
		auto& objects = m_scene->getObjects();

		auto& taskManager = TaskManager::getInstance();

		// Divide the updates in max groups
		std::vector<Ptr<Task>> tasks;
		tasks.reserve(threadCount);

		size_t firstIndex = 0;
		size_t size = objects.size() / threadCount;

		for (size_t i = 0; i < threadCount; i++)
		{
			auto lastIndex = firstIndex + size;

			if (i == threadCount - 1)
			{
				const auto remainingSize = objects.size() - lastIndex;

				lastIndex += remainingSize;
			}

			auto task = taskManager.createTask([this, firstIndex, lastIndex, &objects, frameIndex]()
				{
					for (size_t i = firstIndex; i < lastIndex; i++)
					{
						auto& object = objects[i];
						auto buffer = m_objectFrameData[i].getBuffer(frameIndex);

						UniformObjectElement objectTransforms;
						objectTransforms.model = object.transform;

						void* data = buffer->map();

						memcpy(data, static_cast<void*>(&objectTransforms), sizeof(UniformObjectElement));

						buffer->unmap();
					}
				});

			tasks.push_back(task);

			firstIndex += size;
		}

		for (auto& task : tasks)
			task->wait();
	}
}
<commit_msg>Examples - Sandbox - Removed unused code<commit_after>/*
	Copyright 2021 Jordi SUBIRANA

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to use,
	copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
	Software, and to permit persons to whom the Software is furnished to do so, subject
	to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
	PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
	CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
	OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "BasicRenderPipeline.hpp"

#include "Resources.hpp"

using namespace at;

namespace
{
	constexpr size_t targetThreadCount = 4;

	const size_t threadCount = std::min(targetThreadCount, TaskManager::getInstance().getSize());
}

BasicRenderPipeline::BasicRenderPipeline(const RenderPipeline::Settings& settings) :
	RenderPipeline(settings),
	m_maxFramesInFlight(settings.maxFramesInFlight),
	m_totalTime(0.0f)
{
	const auto windowSize = settings.window->getSize();

	//----- OBJECT RESOURCES -----//

	// Descriptor set layout
	{
		DescriptorSetLayout::Settings descriptorSetLayoutSettings;
		descriptorSetLayoutSettings.bindings =
		{
			{ DescriptorType::UniformBuffer, 0, 1, ShaderStage::Vertex },
			{ DescriptorType::CombinedImageSampler, 1, 1, ShaderStage::Fragment }
		};

		m_objectDescriptorSetLayout = DescriptorSetLayout::create(descriptorSetLayoutSettings);
	}

	// Descriptor pool
	{
		DescriptorPool::Settings descriptorPoolSettings;
		descriptorPoolSettings.layout = m_objectDescriptorSetLayout;
		descriptorPoolSettings.pageSize = m_maxFramesInFlight * objectCount;

		m_objectDescriptorPool = DescriptorPool::create(descriptorPoolSettings);
	}

	//----- FRAME RESOURCES -----//

	// Descriptor set layout
	{
		DescriptorSetLayout::Settings descriptorSetLayoutSettings;
		descriptorSetLayoutSettings.bindings =
		{
			{ DescriptorType::UniformBuffer, 0, 1, ShaderStage::Vertex }
		};

		m_frameDescriptorSetLayout = DescriptorSetLayout::create(descriptorSetLayoutSettings);
	}

	// Descriptor pool
	{
		DescriptorPool::Settings descriptorPoolSettings;
		descriptorPoolSettings.layout = m_frameDescriptorSetLayout;
		descriptorPoolSettings.pageSize = m_maxFramesInFlight;

		m_frameDescriptorPool = DescriptorPool::create(descriptorPoolSettings);
	}

	// Uniform buffers & descriptor sets
	m_frameUniformBuffers.reserve(m_maxFramesInFlight);
	m_frameDescriptorSets.reserve(m_maxFramesInFlight);

	for (uint32_t j = 0; j < m_maxFramesInFlight; j++)
	{
		auto uniformBuffer = Buffer::create({ BufferUsage::Uniform, sizeof(UniformFrameElement), true });

		m_frameUniformBuffers.push_back(uniformBuffer);

		auto descriptorSet = m_frameDescriptorPool->createSet();

		descriptorSet->update(0, uniformBuffer);

		m_frameDescriptorSets.push_back(descriptorSet);
	}

	//----- THREAD RESOURCES -----//
	auto& taskManager = TaskManager::getInstance();
	const auto coreCount = taskManager.getSize();

	m_threadCommandBuffers.resize(coreCount);

	for (auto& commandBuffers : m_threadCommandBuffers)
		commandBuffers.resize(m_maxFramesInFlight);

	m_threadCommandPools.reserve(coreCount);
	for (size_t i = 0; i < coreCount; i++)
		m_threadCommandPools.push_back(CommandPool::create({}));

	//----- PIPELINE RESOURCES -----//

	// Graphics pipeline
	GraphicsPipeline::Settings pipelineSettings;
	pipelineSettings.viewport.size.x = static_cast<float>(windowSize.x);
	pipelineSettings.viewport.size.y = static_cast<float>(windowSize.y);
	pipelineSettings.scissor.size = windowSize;
	pipelineSettings.vertexShader = Shader::create({ vertShaderPath });
	pipelineSettings.fragmentShader = Shader::create({ fragShaderPath });
	pipelineSettings.renderPass = getRenderPass();
	pipelineSettings.descriptorSetLayouts = { m_frameDescriptorSetLayout, m_objectDescriptorSetLayout };
	pipelineSettings.vertexInput.attributes =
	{
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RG32_SFLOAT }
	};
	pipelineSettings.vertexInput.inputs =
	{
		{ 0, 0 },
		{ 0, 1 },
		{ 0, 2 }
	};

	m_pipeline = GraphicsPipeline::create(pipelineSettings);

	loadScene();
}

BasicRenderPipeline::~BasicRenderPipeline()
{
	Renderer::getInstance().waitForIdle();

	// Thread resources
	m_threadCommandBuffers.clear();
	m_threadCommandPools.clear();

	// Frame resources
	m_frameDescriptorSetLayout.reset();
	m_frameDescriptorSets.clear();
	m_frameDescriptorPool.reset();
	m_frameUniformBuffers.clear();

	// Object resources
	m_objectDescriptorSetLayout.reset();
	m_objectDescriptorPool.reset();
	m_objectFrameData.clear();
	m_scene.reset();

	m_pipeline.reset();
}

void BasicRenderPipeline::resize(const Vector2u& size)
{
	m_pipeline.reset();

	GraphicsPipeline::Settings pipelineSettings;
	pipelineSettings.viewport.size.x = static_cast<float>(size.x);
	pipelineSettings.viewport.size.y = static_cast<float>(size.y);
	pipelineSettings.scissor.size = size;
	pipelineSettings.vertexShader = Shader::create({ rscPath / "Shaders/vert.spv" });
	pipelineSettings.fragmentShader = Shader::create({ rscPath / "Shaders/frag.spv" });
	pipelineSettings.renderPass = getRenderPass();
	pipelineSettings.descriptorSetLayouts = { m_frameDescriptorSetLayout, m_objectDescriptorSetLayout };
	pipelineSettings.vertexInput.attributes =
	{
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RGB32_SFLOAT },
		{ VertexAttribute::Format::RG32_SFLOAT }
	};
	pipelineSettings.vertexInput.inputs =
	{
		{ 0, 0 },
		{ 0, 1 },
		{ 0, 2 }
	};

	m_pipeline = GraphicsPipeline::create(pipelineSettings);
}

void BasicRenderPipeline::updateFrame(at::TimeStep elapsedTime)
{
	ATEMA_BENCHMARK("Scene::updateObjects")

	m_totalTime += elapsedTime.getSeconds();

	m_scene->updateObjects(elapsedTime, threadCount);
}

void BasicRenderPipeline::setupFrame(uint32_t frameIndex, Ptr<CommandBuffer> commandBuffer)
{
	updateUniformBuffers(frameIndex);

	auto& globalDescriptorSet = m_frameDescriptorSets[frameIndex];

	beginRenderPass(true);

	// Update objects buffers
	{
		ATEMA_BENCHMARK("CommandBuffers fill");

		// Clear previous command buffers
		for (auto& threadCommandBuffers : m_threadCommandBuffers)
		{
			threadCommandBuffers[frameIndex].clear();
		}

		auto& objects = m_scene->getObjects();

		auto& taskManager = TaskManager::getInstance();

		std::vector<Ptr<Task>> tasks;
		tasks.reserve(threadCount);

		std::vector<Ptr<CommandBuffer>> commandBuffers;
		commandBuffers.resize(threadCount);

		size_t firstIndex = 0;
		size_t size = objects.size() / threadCount;

		for (size_t taskIndex = 0; taskIndex < threadCount; taskIndex++)
		{
			auto lastIndex = firstIndex + size;

			if (taskIndex == threadCount - 1)
			{
				const auto remainingSize = objects.size() - lastIndex;

				lastIndex += remainingSize;
			}

			auto task = taskManager.createTask([this, taskIndex, firstIndex, lastIndex, &objects, &commandBuffers, globalDescriptorSet, frameIndex](size_t threadIndex)
				{
					auto& commandPool = m_threadCommandPools[threadIndex];
					auto commandBuffer = CommandBuffer::create({ commandPool, true, true });

					commandBuffer->beginSecondary(getRenderPass(), getCurrentFramebuffer());

					commandBuffer->bindPipeline(m_pipeline);

					for (size_t i = firstIndex; i < lastIndex; i++)
					{
						auto& object = objects[i];
						auto descriptorSet = m_objectFrameData[i].getDescriptorSet(frameIndex);

						commandBuffer->bindVertexBuffer(object.vertexBuffer, 0);

						commandBuffer->bindIndexBuffer(object.indexBuffer, IndexType::U32);

						commandBuffer->bindDescriptorSets({ globalDescriptorSet, descriptorSet });

						commandBuffer->drawIndexed(object.indexCount);
					}

					commandBuffer->end();

					m_threadCommandBuffers[threadIndex][frameIndex].push_back(commandBuffer);

					commandBuffers[taskIndex] = commandBuffer;
				});

			tasks.push_back(task);

			firstIndex += size;
		}

		for (auto& task : tasks)
			task->wait();

		commandBuffer->executeSecondaryCommands(commandBuffers);
	}

	endRenderPass();
}

void BasicRenderPipeline::loadScene()
{
	ATEMA_BENCHMARK("BasicRenderPipeline::loadScene")

	auto& commandPool = getCommandPools()[0];

	m_scene = std::make_shared<Scene>(commandPool);

	auto& objects = m_scene->getObjects();

	m_objectFrameData.reserve(objects.size());

	for (auto& object : objects)
	{
		ObjectFrameData objectFrameData(object, m_maxFramesInFlight, m_objectDescriptorPool);

		m_objectFrameData.push_back(objectFrameData);
	}
}

void BasicRenderPipeline::updateUniformBuffers(uint32_t frameIndex)
{
	ATEMA_BENCHMARK("BasicRenderPipeline::updateUniformBuffers")

		// Update global buffers
	{
		const auto windowSize = getWindow()->getSize();

		const auto angle = m_totalTime * zoomSpeed;

		const auto sin = std::sin(angle);
		const auto sinSlow = std::sin(angle / 2.0f + 3.14159f);

		const auto sign = (sin + 1.0f) / 2.0f;
		const auto signSlow = (sinSlow + 1.0f) / 2.0f;

		auto radius = modelScale * objectRow;
		radius = sign * radius + (1.0f - sign) * zoomOffset;

		const auto pos = toCartesian({ radius, angle / 3.0f });

		const auto z =  signSlow * radius + (1.0f - signSlow) * zoomOffset;

		const Vector3f cameraPos(pos.x, pos.y, z);
		const Vector3f cameraTarget(0.0f, 0.0f, zoomOffset / 2.0f);
		const Vector3f cameraUp(0.0f, 0.0f, 1.0f);

		UniformFrameElement frameTransforms;
		frameTransforms.view = lookAt(cameraPos, cameraTarget, cameraUp);
		frameTransforms.proj = perspective(toRadians(45.0f), static_cast<float>(windowSize.x) / static_cast<float>(windowSize.y), 0.1f, 1000.0f);
		frameTransforms.proj[1][1] *= -1;

		auto buffer = m_frameUniformBuffers[frameIndex];

		void* data = buffer->map();

		memcpy(data, static_cast<void*>(&frameTransforms), sizeof(UniformFrameElement));

		buffer->unmap();
	}

	// Update objects buffers
	{
		auto& objects = m_scene->getObjects();

		auto& taskManager = TaskManager::getInstance();

		// Divide the updates in max groups
		std::vector<Ptr<Task>> tasks;
		tasks.reserve(threadCount);

		size_t firstIndex = 0;
		size_t size = objects.size() / threadCount;

		for (size_t i = 0; i < threadCount; i++)
		{
			auto lastIndex = firstIndex + size;

			if (i == threadCount - 1)
			{
				const auto remainingSize = objects.size() - lastIndex;

				lastIndex += remainingSize;
			}

			auto task = taskManager.createTask([this, firstIndex, lastIndex, &objects, frameIndex]()
				{
					for (size_t i = firstIndex; i < lastIndex; i++)
					{
						auto& object = objects[i];
						auto buffer = m_objectFrameData[i].getBuffer(frameIndex);

						UniformObjectElement objectTransforms;
						objectTransforms.model = object.transform;

						void* data = buffer->map();

						memcpy(data, static_cast<void*>(&objectTransforms), sizeof(UniformObjectElement));

						buffer->unmap();
					}
				});

			tasks.push_back(task);

			firstIndex += size;
		}

		for (auto& task : tasks)
			task->wait();
	}
}
<|endoftext|>


<commit_before>#pragma once

namespace color_coded
{
  namespace env
  { struct tag{}; }

  template <typename T>
  struct environment;
}

#include "impl.hpp"
<commit_msg>Path is no longer relative; proper path to impl.hpp<commit_after>#pragma once

namespace color_coded
{
  namespace env
  { struct tag{}; }

  template <typename T>
  struct environment;
}

#include "env/impl.hpp"
<|endoftext|>


<commit_before>/*
* Copyright (c) 2014 Eran Pe'er.
*
* This program is made available under the terms of the MIT License.
*
* Created on Mar 10, 2014
*/

#ifndef MockImpl_h__
#define MockImpl_h__

#include <type_traits>
#include <unordered_set>

#include <memory>

#include "mockutils/DynamicProxy.hpp"
#include "fakeit/StubbingImpl.hpp"
#include "fakeit/DomainObjects.hpp"
#include "fakeit/FakeIt.hpp"

namespace fakeit {

	template<typename C, typename ... baseclasses>
	class MockImpl : public MockObject<C>, public virtual ActualInvocationsSource {
	public:

		MockImpl(C &obj) :MockImpl<C, baseclasses...>(obj, true) {
		}

		MockImpl() : MockImpl<C, baseclasses...>(*(createFakeInstance()), false) {
		}

		virtual ~MockImpl() {
			if (isSpy)
				return;
			FakeObject<C, baseclasses...>* fake = (FakeObject<C, baseclasses...>*) instance;
			delete fake;
		}

		/**
		* Return all actual invocations of this mock.
		*/
		void getActualInvocations(std::unordered_set<Invocation*>& into) const override {
			std::vector<ActualInvocationsSource*> vec;
			proxy.getMethodMocks(vec);
			for (ActualInvocationsSource * s : vec) {
				s->getActualInvocations(into);
			}
		}

		void reset() {
			proxy.Reset();
			if (!isSpy) {
				FakeObject<C, baseclasses...>* fake = (FakeObject<C, baseclasses...>*) instance;
				fake->initializeDataMembersArea();
			}
		}

		virtual C& get() override {
			return proxy.get();
		}

		template<class DATA_TYPE, typename ... arglist>
		DataMemberStubbingRoot<C, DATA_TYPE> stubDataMember(DATA_TYPE C::*member, const arglist&... ctorargs) {
			proxy.stubDataMember(member, ctorargs...);
			return DataMemberStubbingRoot<C, DATA_TYPE>();
		}

		template<typename R, typename ... arglist, class = typename std::enable_if<!std::is_void<R>::value>::type>
		FunctionStubbingRoot<C, R, arglist...> stub(R(C::*vMethod)(arglist...)) {
			return FunctionStubbingRoot<C, R, arglist...>(
				std::shared_ptr<MethodStubbingContext<C, R, arglist...>>(new MethodStubbingContextImpl<R, arglist...>(*this, vMethod)));
		}

		template<typename R, typename ... arglist, class = typename std::enable_if<std::is_void<R>::value>::type>
		ProcedureStubbingRoot<C, R, arglist...> stub(R(C::*vMethod)(arglist...)) {
			return ProcedureStubbingRoot<C, R, arglist...>(
				std::shared_ptr<MethodStubbingContext<C, R, arglist...>>(new MethodStubbingContextImpl<R, arglist...>(*this, vMethod)));
		}

	private:
		DynamicProxy<C, baseclasses...> proxy;
		C* instance; //
		bool isSpy;

		template<typename R, typename ... arglist>
		class MethodStubbingContextImpl : public MethodStubbingContext<C, R, arglist...> {
			MockImpl<C, baseclasses...>& mock;
			typename MethodStubbingContext<C, R, arglist...>::MethodType vMethod;
		public:

			MethodStubbingContextImpl(MockImpl<C, baseclasses...>& mock, R(C::*vMethod)(arglist...)) :
				mock(mock), vMethod(vMethod) {
			}

			virtual MethodMock<C, R, arglist...>& getMethodMock() override {
				return mock.stubMethodIfNotStubbed(mock.proxy, vMethod);
			}

			virtual void getActualInvocations(std::unordered_set<Invocation*>& into) const override {
				mock.getActualInvocations(into);
			}

			virtual typename MethodStubbingContext<C, R, arglist...>::MethodType getOriginalMethod() override {
				void * mPtr = mock.getOriginalMethod(vMethod);
				return union_cast<typename MethodStubbingContext<C, R, arglist...>::MethodType>(mPtr);
			}

			virtual MockObject<C>& getMock() override {
				return mock;
			}

		};

		void unmocked() {
			struct UnmockedMethodInvocation : public UnexpectedMethodCallException {
				UnmockedMethodInvocation() :UnexpectedMethodCallException( //
				std::shared_ptr<Invocation> {new ActualInvocation<>(nextInvocationOrdinal(), UnknownMethod::instance())} //
				){} //
			} e;
			FakeIt::getInstance().handle(e);
			throw e;
		}

		static C* createFakeInstance() {
			FakeObject<C, baseclasses...>* fake = new FakeObject<C, baseclasses...>();
			fake->initializeDataMembersArea();
			void* unmockedMethodStubPtr = union_cast<void*>(&MockImpl<C, baseclasses...>::unmocked);
			fake->getVirtualTable().initAll(unmockedMethodStubPtr);
			return (C*) fake;
		}

		template<typename R, typename ... arglist>
		void * getOriginalMethod(R(C::*vMethod)(arglist...)) {
			auto vt = proxy.getOriginalVT();
			auto offset = VTUtils::getOffset(vMethod);
			void * origMethodPtr = vt.getMethod(offset);
			return origMethodPtr;
		}

		template<typename R, typename ... arglist>
		MethodMock<C, R, arglist...>& stubMethodIfNotStubbed(DynamicProxy<C, baseclasses...> &proxy, R(C::*vMethod)(arglist...)) {
			if (!proxy.isStubbed(vMethod)) {
				auto methodMock = std::shared_ptr<MethodInvocationHandler<R, arglist...>> { new MethodMock<C, R, arglist...>(*this, vMethod) };
				proxy.stubMethod(vMethod, methodMock);
			}
			Destructable * d = proxy.getMethodMock(vMethod);
			MethodMock<C, R, arglist...> * methodMock = dynamic_cast<MethodMock<C, R, arglist...> *>(d);
			return *methodMock;
		}

		MockImpl(C &obj, bool isSpy) :
			proxy{ obj }, instance(&obj), isSpy(isSpy) {
		}
	};
}

#endif<commit_msg>cleanup. fix data member names<commit_after>/*
* Copyright (c) 2014 Eran Pe'er.
*
* This program is made available under the terms of the MIT License.
*
* Created on Mar 10, 2014
*/

#ifndef MockImpl_h__
#define MockImpl_h__

#include <type_traits>
#include <unordered_set>

#include <memory>

#include "mockutils/DynamicProxy.hpp"
#include "fakeit/StubbingImpl.hpp"
#include "fakeit/DomainObjects.hpp"
#include "fakeit/FakeIt.hpp"

namespace fakeit {

	template<typename C, typename ... baseclasses>
	class MockImpl : public MockObject<C>, public virtual ActualInvocationsSource {
	public:

		MockImpl(C &obj) :MockImpl<C, baseclasses...>(obj, true) {
		}

		MockImpl() : MockImpl<C, baseclasses...>(*(createFakeInstance()), false) {
		}

		virtual ~MockImpl() {
			if (_isSpy)
				return;
			FakeObject<C, baseclasses...>* fake = (FakeObject<C, baseclasses...>*) _instance;
			delete fake;
		}

		/**
		* Return all actual invocations of this mock.
		*/
		void getActualInvocations(std::unordered_set<Invocation*>& into) const override {
			std::vector<ActualInvocationsSource*> vec;
			_proxy.getMethodMocks(vec);
			for (ActualInvocationsSource * s : vec) {
				s->getActualInvocations(into);
			}
		}

		void reset() {
			_proxy.Reset();
			if (!_isSpy) {
				FakeObject<C, baseclasses...>* fake = (FakeObject<C, baseclasses...>*) _instance;
				fake->initializeDataMembersArea();
			}
		}

		virtual C& get() override {
			return _proxy.get();
		}

		template<class DATA_TYPE, typename ... arglist>
		DataMemberStubbingRoot<C, DATA_TYPE> stubDataMember(DATA_TYPE C::*member, const arglist&... ctorargs) {
			_proxy.stubDataMember(member, ctorargs...);
			return DataMemberStubbingRoot<C, DATA_TYPE>();
		}

		template<typename R, typename ... arglist, class = typename std::enable_if<!std::is_void<R>::value>::type>
		FunctionStubbingRoot<C, R, arglist...> stub(R(C::*vMethod)(arglist...)) {
			return FunctionStubbingRoot<C, R, arglist...>(
				std::shared_ptr<MethodStubbingContext<C, R, arglist...>>(new MethodStubbingContextImpl<R, arglist...>(*this, vMethod)));
		}

		template<typename R, typename ... arglist, class = typename std::enable_if<std::is_void<R>::value>::type>
		ProcedureStubbingRoot<C, R, arglist...> stub(R(C::*vMethod)(arglist...)) {
			return ProcedureStubbingRoot<C, R, arglist...>(
				std::shared_ptr<MethodStubbingContext<C, R, arglist...>>(new MethodStubbingContextImpl<R, arglist...>(*this, vMethod)));
		}

	private:
		DynamicProxy<C, baseclasses...> _proxy;
		C* _instance; //
		bool _isSpy;

		template<typename R, typename ... arglist>
		class MethodStubbingContextImpl : public MethodStubbingContext<C, R, arglist...> {
			MockImpl<C, baseclasses...>& mock;
			typename MethodStubbingContext<C, R, arglist...>::MethodType vMethod;
		public:

			MethodStubbingContextImpl(MockImpl<C, baseclasses...>& mock, R(C::*vMethod)(arglist...)) :
				mock(mock), vMethod(vMethod) {
			}

			virtual MethodMock<C, R, arglist...>& getMethodMock() override {
				return mock.stubMethodIfNotStubbed(mock._proxy, vMethod);
			}

			virtual void getActualInvocations(std::unordered_set<Invocation*>& into) const override {
				mock.getActualInvocations(into);
			}

			virtual typename MethodStubbingContext<C, R, arglist...>::MethodType getOriginalMethod() override {
				void * mPtr = mock.getOriginalMethod(vMethod);
				return union_cast<typename MethodStubbingContext<C, R, arglist...>::MethodType>(mPtr);
			}

			virtual MockObject<C>& getMock() override {
				return mock;
			}

		};

		void unmocked() {
			struct UnmockedMethodInvocation : public UnexpectedMethodCallException {
				UnmockedMethodInvocation() :UnexpectedMethodCallException( //
				std::shared_ptr<Invocation> {new ActualInvocation<>(nextInvocationOrdinal(), UnknownMethod::instance())} //
				){} //
			} e;
			FakeIt::getInstance().handle(e);
			throw e;
		}

		static C* createFakeInstance() {
			FakeObject<C, baseclasses...>* fake = new FakeObject<C, baseclasses...>();
			fake->initializeDataMembersArea();
			void* unmockedMethodStubPtr = union_cast<void*>(&MockImpl<C, baseclasses...>::unmocked);
			fake->getVirtualTable().initAll(unmockedMethodStubPtr);
			return (C*) fake;
		}

		template<typename R, typename ... arglist>
		void * getOriginalMethod(R(C::*vMethod)(arglist...)) {
			auto vt = _proxy.getOriginalVT();
			auto offset = VTUtils::getOffset(vMethod);
			void * origMethodPtr = vt.getMethod(offset);
			return origMethodPtr;
		}

		template<typename R, typename ... arglist>
		MethodMock<C, R, arglist...>& stubMethodIfNotStubbed(DynamicProxy<C, baseclasses...> &proxy, R(C::*vMethod)(arglist...)) {
			if (!proxy.isStubbed(vMethod)) {
				auto methodMock = std::shared_ptr<MethodInvocationHandler<R, arglist...>> { new MethodMock<C, R, arglist...>(*this, vMethod) };
				proxy.stubMethod(vMethod, methodMock);
			}
			Destructable * d = proxy.getMethodMock(vMethod);
			MethodMock<C, R, arglist...> * methodMock = dynamic_cast<MethodMock<C, R, arglist...> *>(d);
			return *methodMock;
		}

		MockImpl(C &obj, bool isSpy) :
			_proxy{ obj }, _instance(&obj), _isSpy(_isSpy) {
		}
	};
}

#endif<|endoftext|>