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

#include "itkMacro.h"
#include "itkGPUReduction.h"

//#define CPU_VERIFY

namespace itk
{
/**
 * Default constructor
 */
template< typename TElement >
GPUReduction< TElement >
::GPUReduction()
{
  /*** Prepare GPU opencl program ***/
  m_GPUKernelManager = GPUKernelManager::New();
  m_GPUDataManager = NULL;

}
template< typename TElement >
GPUReduction< TElement >
::~GPUReduction()
{
  this->ReleaseGPUInputBuffer();
}

/**
 * Standard "PrintSelf" method.
 */
template< typename TElement >
void
GPUReduction< TElement >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  //GetTypenameInString( typeid(TElement), os);
}

template< typename TElement >
unsigned int
GPUReduction< TElement >
::NextPow2( unsigned int x ) {
    --x;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return ++x;
}

template< typename TElement >
bool
GPUReduction< TElement >
::isPow2(unsigned int x)
{
    return ((x&(x-1)) == 0);
}

template< typename TElement >
void
GPUReduction< TElement >
::GetNumBlocksAndThreads(int whichKernel, int n, int maxBlocks, int maxThreads, int &blocks, int &threads)
{
    if (whichKernel < 3)
    {
        threads = (n < maxThreads) ? this->NextPow2(n) : maxThreads;
        blocks = (n + threads - 1) / threads;
    }
    else
    {
        threads = (n < maxThreads*2) ? this->NextPow2((n + 1)/ 2) : maxThreads;
        blocks = (n + (threads * 2 - 1)) / (threads * 2);
    }

    if (whichKernel == 6)
    {
      if (maxBlocks < blocks)
      {
        blocks = maxBlocks;
      }
    }
}

template< typename TElement >
unsigned int
GPUReduction< TElement >
::GetReductionKernel(int whichKernel, int blockSize, int isPowOf2)
{
  if ( whichKernel != 5 && whichKernel != 6 )
    {
    itkExceptionMacro(<< "Reduction kernel undefined!");
    return 0;
    }

  std::ostringstream defines;

  defines << "#define blockSize " << blockSize << std::endl;
  defines << "#define nIsPow2 " << isPowOf2 << std::endl;

  defines << "#define T ";
  GetTypenameInString( typeid ( TElement ), defines );

  std::cout << "Defines: " << defines.str() << std::endl;

  const char* GPUSource = GPUReduction::GetOpenCLSource();

  // load and build program
  this->m_GPUKernelManager->LoadProgramFromString( GPUSource, defines.str().c_str() );

  std::ostringstream kernelName;
  kernelName << "reduce" << whichKernel;
  unsigned int handle = this->m_GPUKernelManager->CreateKernel(kernelName.str().c_str());

  size_t wgSize;
  cl_int ciErrNum = this->m_GPUKernelManager->GetKernelWorkGroupInfo(handle, CL_KERNEL_WORK_GROUP_SIZE, &wgSize);
  OpenCLCheckError(ciErrNum, __FILE__, __LINE__, ITK_LOCATION);

  m_SmallBlock = (wgSize == 64);

  // NOTE: the program will get deleted when the kernel is also released
  //this->m_GPUKernelManager->ReleaseProgram();

  return handle;
}

template< typename TElement >
void
GPUReduction< TElement >
::AllocateGPUInputBuffer(TElement *h_idata)
{
  unsigned int bytes = m_Size * sizeof(TElement);

  m_GPUDataManager = GPUDataManager::New();
  m_GPUDataManager->SetBufferSize( bytes );
  m_GPUDataManager->SetCPUBufferPointer( h_idata );
  m_GPUDataManager->Allocate();

  if (h_idata)
    {
    m_GPUDataManager->SetGPUDirtyFlag(true);
    }
}

template< typename TElement >
void
GPUReduction< TElement >
::ReleaseGPUInputBuffer()
{
  if (m_GPUDataManager == (GPUDataPointer)NULL)
    {
    return;
    }

  m_GPUDataManager->Initialize();
}

template< typename TElement >
TElement
GPUReduction< TElement >
::RandomTest()
{
  int size = (1<<24)-1917;    // number of elements to reduce

  this->InitializeKernel(size);

  unsigned int bytes = size * sizeof(TElement);
  TElement*    h_idata = (TElement*)malloc(bytes);

  for(int i=0; i<size; i++)
  {
      // Keep the numbers small so we don't get truncation error in the sum
      h_idata[i] = (TElement)(rand() & 0xFF);
  }

  this->AllocateGPUInputBuffer(h_idata);

  TElement gpu_result = this->GPUGenerateData();
  std::cout << "GPU result = " << gpu_result << std::endl << std::flush;

  TElement cpu_result = this->CPUGenerateData(h_idata, size);
  std::cout << "CPU result = " << cpu_result << std::endl;

  this->ReleaseGPUInputBuffer();

  free(h_idata);

  return 0;
}

template< typename TElement >
void
GPUReduction< TElement >
::InitializeKernel(unsigned int size)
{
  m_Size = size;

  // Create a testing kernel to decide block size
//   m_TestGPUKernelHandle = this->GetReductionKernel(6, 64, 1);
  //m_GPUKernelManager->ReleaseKernel(kernelHandle);

  // number of threads per block
  int maxThreads = m_SmallBlock ? 64 : 128;

  int whichKernel = 6;
  int maxBlocks = 64;

  int numBlocks = 0;
  int numThreads = 0;

  this->GetNumBlocksAndThreads(whichKernel, size, maxBlocks, maxThreads, numBlocks, numThreads);

  m_ReduceGPUKernelHandle = this->GetReductionKernel(whichKernel, numThreads, isPow2(size) );

}

template< typename TElement >
TElement
GPUReduction< TElement >
::GPUGenerateData()
{
  unsigned int size = m_Size;

  // number of threads per block
  int maxThreads = m_SmallBlock ? 64 : 128;

  int whichKernel = 6;
  int maxBlocks = 64;
  bool cpuFinalReduction = true;
  int  cpuFinalThreshold = 1;

  int numBlocks = 0;
  int numThreads = 0;

  this->GetNumBlocksAndThreads(whichKernel, size, maxBlocks, maxThreads, numBlocks, numThreads);

  if (numBlocks == 1) cpuFinalThreshold = 1;

  // allocate output data for the result
  TElement* h_odata = (TElement*)malloc(numBlocks * sizeof(TElement));

  GPUDataPointer odata = GPUDataManager::New();
  odata->SetBufferSize( numBlocks * sizeof(TElement) );
  odata->SetCPUBufferPointer( h_odata );
  odata->Allocate();
  odata->SetCPUDirtyFlag(true);

  double dTotalTime = 0.0;

  m_GPUResult = 0;
  m_GPUResult = this->GPUReduce(size, numThreads, numBlocks, maxThreads, maxBlocks,
                                  whichKernel, cpuFinalReduction,
                                  cpuFinalThreshold, &dTotalTime,
                                  m_GPUDataManager, odata);

  // cleanup
  free(h_odata);

  return m_GPUResult;
}

template< typename TElement >
TElement
GPUReduction< TElement >
::GPUReduce(  cl_int  n,
              int  numThreads,
              int  numBlocks,
              int  itkNotUsed(maxThreads),
              int  itkNotUsed(maxBlocks),
              int  itkNotUsed(whichKernel),
              bool itkNotUsed(cpuFinalReduction),
              int  itkNotUsed(cpuFinalThreshold),
              double* itkNotUsed(dTotalTime),
              GPUDataPointer idata,
              GPUDataPointer odata)
{
  TElement gpu_result = 0;

  // arguments set up
  int argidx = 0;

  this->m_GPUKernelManager->SetKernelArgWithImage(m_ReduceGPUKernelHandle, argidx++, idata);
  this->m_GPUKernelManager->SetKernelArgWithImage(m_ReduceGPUKernelHandle, argidx++, odata);

  this->m_GPUKernelManager->SetKernelArg(m_ReduceGPUKernelHandle, argidx++, sizeof(cl_int), &n);
  //shared memory below
  this->m_GPUKernelManager->SetKernelArg(m_ReduceGPUKernelHandle, argidx++, sizeof(TElement) * numThreads, NULL);

  size_t globalSize[1];
  size_t localSize[1];

  gpu_result = 0;

  // execute the kernel
  globalSize[0] = numBlocks * numThreads;
  localSize[0] = numThreads;

  this->m_GPUKernelManager->LaunchKernel(m_ReduceGPUKernelHandle, 1, globalSize, localSize );

  odata->SetCPUDirtyFlag(true);
  TElement* h_odata = (TElement*)odata->GetCPUBufferPointer();

#ifdef CPU_VERIFY
  idata->SetCPUDirtyFlag(true);
  TElement* h_idata = (TElement*)idata->GetCPUBufferPointer(); //debug
  if (!h_idata)
  {
    h_idata = (TElement*)malloc(sizeof(TElement) * n);
    idata->SetCPUBufferPointer(h_idata);
    idata->SetCPUDirtyFlag(true);
    h_idata = (TElement*)idata->GetCPUBufferPointer(); //debug
  }

  TElement CPUSum = this->CPUGenerateData(h_idata, n);
  std::cout << "CPU_VERIFY sum = " << CPUSum << std::endl;
#endif

  for(int i=0; i<numBlocks; i++)
  {
      gpu_result += h_odata[i];
  }

  // Release the kernels
  // clReleaseKernel(reductionKernel);

  return gpu_result;
}

template< typename TElement >
TElement
GPUReduction< TElement >
::CPUGenerateData(TElement *data, int size)
{
    TElement sum = data[0];
//     TElement c = (TElement)0.0;
    for (int i = 1; i < size; i++)
    {
    // TODO consider using compensated sum algorithm

    sum = sum + data[i];
    }
    m_CPUResult = sum;
    return sum;
}

} // end namespace itk

#endif
<commit_msg>BUG: initialize m_SmallBlock ivar in ctor; fixes garbage read<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef __itkGPUReduction_hxx
#define __itkGPUReduction_hxx

#include "itkMacro.h"
#include "itkGPUReduction.h"

//#define CPU_VERIFY

namespace itk
{
/**
 * Default constructor
 */
template< typename TElement >
GPUReduction< TElement >
::GPUReduction()
{
  /*** Prepare GPU opencl program ***/
  m_GPUKernelManager = GPUKernelManager::New();
  m_GPUDataManager = NULL;

  m_ReduceGPUKernelHandle = 0;
  m_TestGPUKernelHandle = 0;

  m_Size = 0;
  m_SmallBlock = false;

}
template< typename TElement >
GPUReduction< TElement >
::~GPUReduction()
{
  this->ReleaseGPUInputBuffer();
}

/**
 * Standard "PrintSelf" method.
 */
template< typename TElement >
void
GPUReduction< TElement >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  //GetTypenameInString( typeid(TElement), os);
}

template< typename TElement >
unsigned int
GPUReduction< TElement >
::NextPow2( unsigned int x ) {
    --x;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return ++x;
}

template< typename TElement >
bool
GPUReduction< TElement >
::isPow2(unsigned int x)
{
    return ((x&(x-1)) == 0);
}

template< typename TElement >
void
GPUReduction< TElement >
::GetNumBlocksAndThreads(int whichKernel, int n, int maxBlocks, int maxThreads, int &blocks, int &threads)
{
    if (whichKernel < 3)
    {
        threads = (n < maxThreads) ? this->NextPow2(n) : maxThreads;
        blocks = (n + threads - 1) / threads;
    }
    else
    {
        threads = (n < maxThreads*2) ? this->NextPow2((n + 1)/ 2) : maxThreads;
        blocks = (n + (threads * 2 - 1)) / (threads * 2);
    }

    if (whichKernel == 6)
    {
      if (maxBlocks < blocks)
      {
        blocks = maxBlocks;
      }
    }
}

template< typename TElement >
unsigned int
GPUReduction< TElement >
::GetReductionKernel(int whichKernel, int blockSize, int isPowOf2)
{
  if ( whichKernel != 5 && whichKernel != 6 )
    {
    itkExceptionMacro(<< "Reduction kernel undefined!");
    return 0;
    }

  std::ostringstream defines;

  defines << "#define blockSize " << blockSize << std::endl;
  defines << "#define nIsPow2 " << isPowOf2 << std::endl;

  defines << "#define T ";
  GetTypenameInString( typeid ( TElement ), defines );

  std::cout << "Defines: " << defines.str() << std::endl;

  const char* GPUSource = GPUReduction::GetOpenCLSource();

  // load and build program
  this->m_GPUKernelManager->LoadProgramFromString( GPUSource, defines.str().c_str() );

  std::ostringstream kernelName;
  kernelName << "reduce" << whichKernel;
  unsigned int handle = this->m_GPUKernelManager->CreateKernel(kernelName.str().c_str());

  size_t wgSize;
  cl_int ciErrNum = this->m_GPUKernelManager->GetKernelWorkGroupInfo(handle, CL_KERNEL_WORK_GROUP_SIZE, &wgSize);
  OpenCLCheckError(ciErrNum, __FILE__, __LINE__, ITK_LOCATION);

  m_SmallBlock = (wgSize == 64);

  // NOTE: the program will get deleted when the kernel is also released
  //this->m_GPUKernelManager->ReleaseProgram();

  return handle;
}

template< typename TElement >
void
GPUReduction< TElement >
::AllocateGPUInputBuffer(TElement *h_idata)
{
  unsigned int bytes = m_Size * sizeof(TElement);

  m_GPUDataManager = GPUDataManager::New();
  m_GPUDataManager->SetBufferSize( bytes );
  m_GPUDataManager->SetCPUBufferPointer( h_idata );
  m_GPUDataManager->Allocate();

  if (h_idata)
    {
    m_GPUDataManager->SetGPUDirtyFlag(true);
    }
}

template< typename TElement >
void
GPUReduction< TElement >
::ReleaseGPUInputBuffer()
{
  if (m_GPUDataManager == (GPUDataPointer)NULL)
    {
    return;
    }

  m_GPUDataManager->Initialize();
}

template< typename TElement >
TElement
GPUReduction< TElement >
::RandomTest()
{
  int size = (1<<24)-1917;    // number of elements to reduce

  this->InitializeKernel(size);

  unsigned int bytes = size * sizeof(TElement);
  TElement*    h_idata = (TElement*)malloc(bytes);

  for(int i=0; i<size; i++)
  {
      // Keep the numbers small so we don't get truncation error in the sum
      h_idata[i] = (TElement)(rand() & 0xFF);
  }

  this->AllocateGPUInputBuffer(h_idata);

  TElement gpu_result = this->GPUGenerateData();
  std::cout << "GPU result = " << gpu_result << std::endl << std::flush;

  TElement cpu_result = this->CPUGenerateData(h_idata, size);
  std::cout << "CPU result = " << cpu_result << std::endl;

  this->ReleaseGPUInputBuffer();

  free(h_idata);

  return 0;
}

template< typename TElement >
void
GPUReduction< TElement >
::InitializeKernel(unsigned int size)
{
  m_Size = size;

  // Create a testing kernel to decide block size
//   m_TestGPUKernelHandle = this->GetReductionKernel(6, 64, 1);
  //m_GPUKernelManager->ReleaseKernel(kernelHandle);

  // number of threads per block
  int maxThreads = m_SmallBlock ? 64 : 128;

  int whichKernel = 6;
  int maxBlocks = 64;

  int numBlocks = 0;
  int numThreads = 0;

  this->GetNumBlocksAndThreads(whichKernel, size, maxBlocks, maxThreads, numBlocks, numThreads);

  m_ReduceGPUKernelHandle = this->GetReductionKernel(whichKernel, numThreads, isPow2(size) );

}

template< typename TElement >
TElement
GPUReduction< TElement >
::GPUGenerateData()
{
  unsigned int size = m_Size;

  // number of threads per block
  int maxThreads = m_SmallBlock ? 64 : 128;

  int whichKernel = 6;
  int maxBlocks = 64;
  bool cpuFinalReduction = true;
  int  cpuFinalThreshold = 1;

  int numBlocks = 0;
  int numThreads = 0;

  this->GetNumBlocksAndThreads(whichKernel, size, maxBlocks, maxThreads, numBlocks, numThreads);

  if (numBlocks == 1) cpuFinalThreshold = 1;

  // allocate output data for the result
  TElement* h_odata = (TElement*)malloc(numBlocks * sizeof(TElement));

  GPUDataPointer odata = GPUDataManager::New();
  odata->SetBufferSize( numBlocks * sizeof(TElement) );
  odata->SetCPUBufferPointer( h_odata );
  odata->Allocate();
  odata->SetCPUDirtyFlag(true);

  double dTotalTime = 0.0;

  m_GPUResult = 0;
  m_GPUResult = this->GPUReduce(size, numThreads, numBlocks, maxThreads, maxBlocks,
                                  whichKernel, cpuFinalReduction,
                                  cpuFinalThreshold, &dTotalTime,
                                  m_GPUDataManager, odata);

  // cleanup
  free(h_odata);

  return m_GPUResult;
}

template< typename TElement >
TElement
GPUReduction< TElement >
::GPUReduce(  cl_int  n,
              int  numThreads,
              int  numBlocks,
              int  itkNotUsed(maxThreads),
              int  itkNotUsed(maxBlocks),
              int  itkNotUsed(whichKernel),
              bool itkNotUsed(cpuFinalReduction),
              int  itkNotUsed(cpuFinalThreshold),
              double* itkNotUsed(dTotalTime),
              GPUDataPointer idata,
              GPUDataPointer odata)
{
  TElement gpu_result = 0;

  // arguments set up
  int argidx = 0;

  this->m_GPUKernelManager->SetKernelArgWithImage(m_ReduceGPUKernelHandle, argidx++, idata);
  this->m_GPUKernelManager->SetKernelArgWithImage(m_ReduceGPUKernelHandle, argidx++, odata);

  this->m_GPUKernelManager->SetKernelArg(m_ReduceGPUKernelHandle, argidx++, sizeof(cl_int), &n);
  //shared memory below
  this->m_GPUKernelManager->SetKernelArg(m_ReduceGPUKernelHandle, argidx++, sizeof(TElement) * numThreads, NULL);

  size_t globalSize[1];
  size_t localSize[1];

  gpu_result = 0;

  // execute the kernel
  globalSize[0] = numBlocks * numThreads;
  localSize[0] = numThreads;

  this->m_GPUKernelManager->LaunchKernel(m_ReduceGPUKernelHandle, 1, globalSize, localSize );

  odata->SetCPUDirtyFlag(true);
  TElement* h_odata = (TElement*)odata->GetCPUBufferPointer();

#ifdef CPU_VERIFY
  idata->SetCPUDirtyFlag(true);
  TElement* h_idata = (TElement*)idata->GetCPUBufferPointer(); //debug
  if (!h_idata)
  {
    h_idata = (TElement*)malloc(sizeof(TElement) * n);
    idata->SetCPUBufferPointer(h_idata);
    idata->SetCPUDirtyFlag(true);
    h_idata = (TElement*)idata->GetCPUBufferPointer(); //debug
  }

  TElement CPUSum = this->CPUGenerateData(h_idata, n);
  std::cout << "CPU_VERIFY sum = " << CPUSum << std::endl;
#endif

  for(int i=0; i<numBlocks; i++)
  {
      gpu_result += h_odata[i];
  }

  // Release the kernels
  // clReleaseKernel(reductionKernel);

  return gpu_result;
}

template< typename TElement >
TElement
GPUReduction< TElement >
::CPUGenerateData(TElement *data, int size)
{
    TElement sum = data[0];
//     TElement c = (TElement)0.0;
    for (int i = 1; i < size; i++)
    {
    // TODO consider using compensated sum algorithm

    sum = sum + data[i];
    }
    m_CPUResult = sum;
    return sum;
}

} // end namespace itk

#endif
<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2022 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <inviwopy/pydatareaders.h>

#include <inviwo/core/io/datareader.h>
#include <inviwo/core/io/datareaderfactory.h>
#include <inviwo/core/datastructures/geometry/mesh.h>
#include <inviwo/core/datastructures/image/image.h>
#include <inviwo/core/datastructures/volume/volume.h>

namespace inviwo {

// Allow overriding virtual functions from Python
template <typename T>
class DataReaderTypeTrampoline : public DataReaderType<T>,
                                 public pybind11::trampoline_self_life_support {
public:
    using DataReaderType<T>::DataReaderType;  // Inherit constructors

    virtual DataReaderType<T>* clone() const override {
        PYBIND11_OVERRIDE_PURE(DataReaderType<T>*, /* Return type */
                               DataReaderType<T>,  /* Parent class */
                               clone, /* Name of function in C++ (must match Python name) */
        );
    }
    virtual bool setOption(std::string_view key, std::any value) override {
        PYBIND11_OVERRIDE(bool,              /* Return type */
                          DataReaderType<T>, /* Parent class */
                          setOption,         /* Name of function in C++ (must match Python name) */
                          key, value         /* Argument(s) */
        );
    }
    virtual std::shared_ptr<T> readData(std::string_view filePath) override {
        PYBIND11_OVERRIDE_PURE(std::shared_ptr<T>, /* Return type */
                               DataReaderType<T>,  /* Parent class */
                               readData, /* Name of function in C++ (must match Python name) */
                               filePath  /* Argument(s) */
        );
    }
    virtual std::shared_ptr<T> readData(std::string_view filePath, MetaDataOwner* owner) override {
        PYBIND11_OVERRIDE(std::shared_ptr<T>, /* Return type */
                          DataReaderType<T>,  /* Parent class */
                          readData,           /* Name of function in C++ (must match Python name) */
                          filePath, owner     /* Argument(s) */
        );
    }
};

template <typename T>
void exposeFactoryReaderType(pybind11::class_<DataReaderFactory>& r, std::string_view type) {
    namespace py = pybind11;
    r.def(fmt::format("getExtensionsFor{}", type).c_str(),
          (std::vector<FileExtension>(DataReaderFactory::*)() const) &
              DataReaderFactory::getExtensionsForType<T>)
        .def(fmt::format("get{}Reader", type).c_str(),
             (std::unique_ptr<DataReaderType<T>>(DataReaderFactory::*)(std::string_view) const) &
                 DataReaderFactory::getReaderForTypeAndExtension<T>)
        .def(
            fmt::format("get{}Reader", type).c_str(),
            (std::unique_ptr<DataReaderType<T>>(DataReaderFactory::*)(const FileExtension&) const) &
                DataReaderFactory::getReaderForTypeAndExtension<T>)
        .def(fmt::format("get{}Reader", type).c_str(),
             (std::unique_ptr<DataReaderType<T>>(DataReaderFactory::*)(const FileExtension&,
                                                                       std::string_view) const) &
                 DataReaderFactory::getReaderForTypeAndExtension<T>)
        .def(fmt::format("has{}Reader", type).c_str(),
             (bool (DataReaderFactory::*)(std::string_view) const) &
                 DataReaderFactory::hasReaderForTypeAndExtension<T>);
}

void exposeDataReaders(pybind11::module& m) {
    namespace py = pybind11;

    py::class_<DataReader>(m, "DataReader")
        .def("clone", &DataReader::clone)
        .def_property_readonly("extensions", &DataReader::getExtensions,
                               py::return_value_policy::reference_internal)
        .def("addExtension", &DataReader::addExtension)
        .def("setOption", &DataReader::setOption)
        .def("getOption", &DataReader::getOption);

    // https://pybind11.readthedocs.io/en/stable/advanced/classes.html#binding-classes-with-template-parameters
    py::class_<DataReaderType<Image>, DataReaderTypeTrampoline<Image>>(m, "ImageDataReader")
        .def("readData", py::overload_cast<std::string_view>(&DataReaderType<Image>::readData))
        .def("readData",
             py::overload_cast<std::string_view, MetaDataOwner*>(&DataReaderType<Image>::readData));
    py::class_<DataReaderType<Mesh>, DataReaderTypeTrampoline<Mesh>>(m, "MeshDataReader")
        .def("readData", py::overload_cast<std::string_view>(&DataReaderType<Mesh>::readData))
        .def("readData",
             py::overload_cast<std::string_view, MetaDataOwner*>(&DataReaderType<Mesh>::readData));
    py::class_<DataReaderType<Volume>, DataReaderTypeTrampoline<Volume>>(m, "VolumeDataReader")
        .def("readData", py::overload_cast<std::string_view>(&DataReaderType<Volume>::readData))
        .def("readData", py::overload_cast<std::string_view, MetaDataOwner*>(
                             &DataReaderType<Volume>::readData));

    auto fr =
        py::class_<DataReaderFactory>(m, "DataReaderFactory")
            .def(py::init<>())
            .def("create",
                 (std::unique_ptr<DataReader>(DataReaderFactory::*)(const FileExtension&) const) &
                     DataReaderFactory::create)
            .def("create",
                 (std::unique_ptr<DataReader>(DataReaderFactory::*)(std::string_view) const) &
                     DataReaderFactory::create)
            .def("hasKey",
                 (bool (DataReaderFactory::*)(std::string_view) const) & DataReaderFactory::hasKey)
            .def("hasKey", (bool (DataReaderFactory::*)(const FileExtension&) const) &
                               DataReaderFactory::hasKey);
    // No good way of dealing with template return types so we manually define one for each known
    // type.
    // https://github.com/pybind/pybind11/issues/1667#issuecomment-454348004
    // If your module exposes a new reader type it will have to bind getXXXReader.
    exposeFactoryReaderType<Image>(fr, "Image");
    exposeFactoryReaderType<Mesh>(fr, "Mesh");
    exposeFactoryReaderType<Volume>(fr, "Volume");
}

}  // namespace inviwo
<commit_msg>Python3: Fix pybind DataReader exposure for Image/Mesh/Volume<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2022 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <inviwopy/pydatareaders.h>

#include <inviwo/core/io/datareader.h>
#include <inviwo/core/io/datareaderfactory.h>
#include <inviwo/core/datastructures/geometry/mesh.h>
#include <inviwo/core/datastructures/image/image.h>
#include <inviwo/core/datastructures/volume/volume.h>

namespace inviwo {

// Allow overriding virtual functions from Python
template <typename T>
class DataReaderTypeTrampoline : public DataReaderType<T>,
                                 public pybind11::trampoline_self_life_support {
public:
    using DataReaderType<T>::DataReaderType;  // Inherit constructors

    virtual DataReaderType<T>* clone() const override {
        PYBIND11_OVERRIDE_PURE(DataReaderType<T>*, /* Return type */
                               DataReaderType<T>,  /* Parent class */
                               clone, /* Name of function in C++ (must match Python name) */
        );
    }
    virtual bool setOption(std::string_view key, std::any value) override {
        PYBIND11_OVERRIDE(bool,              /* Return type */
                          DataReaderType<T>, /* Parent class */
                          setOption,         /* Name of function in C++ (must match Python name) */
                          key, value         /* Argument(s) */
        );
    }
    virtual std::shared_ptr<T> readData(std::string_view filePath) override {
        PYBIND11_OVERRIDE_PURE(std::shared_ptr<T>, /* Return type */
                               DataReaderType<T>,  /* Parent class */
                               readData, /* Name of function in C++ (must match Python name) */
                               filePath  /* Argument(s) */
        );
    }
    virtual std::shared_ptr<T> readData(std::string_view filePath, MetaDataOwner* owner) override {
        PYBIND11_OVERRIDE(std::shared_ptr<T>, /* Return type */
                          DataReaderType<T>,  /* Parent class */
                          readData,           /* Name of function in C++ (must match Python name) */
                          filePath, owner     /* Argument(s) */
        );
    }
};

template <typename T>
void exposeFactoryReaderType(pybind11::class_<DataReaderFactory>& r, std::string_view type) {
    namespace py = pybind11;
    r.def(fmt::format("getExtensionsFor{}", type).c_str(),
          (std::vector<FileExtension>(DataReaderFactory::*)() const) &
              DataReaderFactory::getExtensionsForType<T>)
        .def(fmt::format("get{}Reader", type).c_str(),
             (std::unique_ptr<DataReaderType<T>>(DataReaderFactory::*)(std::string_view) const) &
                 DataReaderFactory::getReaderForTypeAndExtension<T>)
        .def(
            fmt::format("get{}Reader", type).c_str(),
            (std::unique_ptr<DataReaderType<T>>(DataReaderFactory::*)(const FileExtension&) const) &
                DataReaderFactory::getReaderForTypeAndExtension<T>)
        .def(fmt::format("get{}Reader", type).c_str(),
             (std::unique_ptr<DataReaderType<T>>(DataReaderFactory::*)(const FileExtension&,
                                                                       std::string_view) const) &
                 DataReaderFactory::getReaderForTypeAndExtension<T>)
        .def(fmt::format("has{}Reader", type).c_str(),
             (bool(DataReaderFactory::*)(std::string_view) const) &
                 DataReaderFactory::hasReaderForTypeAndExtension<T>);
}

void exposeDataReaders(pybind11::module& m) {
    namespace py = pybind11;

    py::class_<DataReader>(m, "DataReader")
        .def("clone", &DataReader::clone)
        .def_property_readonly("extensions", &DataReader::getExtensions,
                               py::return_value_policy::reference_internal)
        .def("addExtension", &DataReader::addExtension)
        .def("setOption", &DataReader::setOption)
        .def("getOption", &DataReader::getOption);

    // https://pybind11.readthedocs.io/en/stable/advanced/classes.html#binding-classes-with-template-parameters
    py::class_<DataReaderType<Image>, DataReader, DataReaderTypeTrampoline<Image>>(
        m, "ImageDataReader")
        .def("readData", py::overload_cast<std::string_view>(&DataReaderType<Image>::readData))
        .def("readData",
             py::overload_cast<std::string_view, MetaDataOwner*>(&DataReaderType<Image>::readData));
    py::class_<DataReaderType<Mesh>, DataReader, DataReaderTypeTrampoline<Mesh>>(m,
                                                                                "MeshDataReader")
        .def("readData", py::overload_cast<std::string_view>(&DataReaderType<Mesh>::readData))
        .def("readData",
             py::overload_cast<std::string_view, MetaDataOwner*>(&DataReaderType<Mesh>::readData));
    py::class_<DataReaderType<Volume>, DataReader, DataReaderTypeTrampoline<Volume>>(
        m, "VolumeDataReader")
        .def("readData", py::overload_cast<std::string_view>(&DataReaderType<Volume>::readData))
        .def("readData", py::overload_cast<std::string_view, MetaDataOwner*>(
                             &DataReaderType<Volume>::readData));

    auto fr =
        py::class_<DataReaderFactory>(m, "DataReaderFactory")
            .def(py::init<>())
            .def("create",
                 (std::unique_ptr<DataReader>(DataReaderFactory::*)(const FileExtension&) const) &
                     DataReaderFactory::create)
            .def("create",
                 (std::unique_ptr<DataReader>(DataReaderFactory::*)(std::string_view) const) &
                     DataReaderFactory::create)
            .def("hasKey",
                 (bool(DataReaderFactory::*)(std::string_view) const) & DataReaderFactory::hasKey)
            .def("hasKey", (bool(DataReaderFactory::*)(const FileExtension&) const) &
                               DataReaderFactory::hasKey);
    // No good way of dealing with template return types so we manually define one for each known
    // type.
    // https://github.com/pybind/pybind11/issues/1667#issuecomment-454348004
    // If your module exposes a new reader type it will have to bind getXXXReader.
    exposeFactoryReaderType<Image>(fr, "Image");
    exposeFactoryReaderType<Mesh>(fr, "Mesh");
    exposeFactoryReaderType<Volume>(fr, "Volume");
}

}  // namespace inviwo
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------
//    @FileName         :   NFCNPCRefreshModule.cpp
//    @Author               :    LvSheng.Huang
//    @Date                 :    2013-10-17
//    @Module               :    NFCNPCRefreshModule
//    @Desc                 :    NPCˢ
// -------------------------------------------------------------------------

#include "NFCNPCRefreshModule.h"
#include "NFComm\NFCore\NFCCommonConfig.h"

bool NFCNPCRefreshModule::Init()
{
    return true;
}

bool NFCNPCRefreshModule::Shut()
{
    return true;
}

bool NFCNPCRefreshModule::Execute()
{
    return true;
}

bool NFCNPCRefreshModule::AfterInit()
{
    m_pKernelModule = pPluginManager->FindModule<NFIKernelModule>( "NFCKernelModule" );
    m_pSceneProcessModule = pPluginManager->FindModule<NFISceneProcessModule>( "NFCSceneProcessModule" );
    m_pElementInfoModule = pPluginManager->FindModule<NFIElementInfoModule>( "NFCElementInfoModule" );
	m_pPackModule = pPluginManager->FindModule<NFIPackModule>("NFCPackModule");
	m_pLogModule = pPluginManager->FindModule<NFILogModule>("NFCLogModule");
	m_pLevelModule = pPluginManager->FindModule<NFILevelModule>("NFCLevelModule");

    assert(NULL != m_pKernelModule);
    assert(NULL != m_pSceneProcessModule);
    assert(NULL != m_pElementInfoModule);
	assert(NULL != m_pPackModule);
	assert(NULL != m_pLogModule);
	assert(NULL != m_pLevelModule);

	m_pKernelModule->AddClassCallBack(NFrame::NPC::ThisName(), this, &NFCNPCRefreshModule::OnObjectClassEvent);
	m_pKernelModule->AddClassCallBack(NFrame::Player::ThisName(), this, &NFCNPCRefreshModule::OnObjectClassEvent);

    return true;
}

int NFCNPCRefreshModule::OnObjectClassEvent( const NFGUID& self, const std::string& strClassName, const CLASS_OBJECT_EVENT eClassEvent, const NFIDataList& var )
{
    NF_SHARE_PTR<NFIObject> pSelf = m_pKernelModule->GetObject(self);
    if (nullptr == pSelf)
    {
        return 1;
    }

    if (strClassName == NFrame::NPC::ThisName())
    {
        if ( CLASS_OBJECT_EVENT::COE_CREATE_LOADDATA == eClassEvent )
        {
            const std::string& strConfigIndex = m_pKernelModule->GetPropertyString(self, NFrame::NPC::ConfigID());
            const std::string& strPropertyID = m_pElementInfoModule->GetPropertyString(strConfigIndex, NFrame::NPC::EffectData());
            NF_SHARE_PTR<NFIPropertyManager> pConfigPropertyManager = m_pElementInfoModule->GetPropertyManager(strPropertyID);
            if (pConfigPropertyManager)
            {
                std::string strProperName;
                NF_SHARE_PTR<NFIPropertyManager> pSelfPropertyManager = pSelf->GetPropertyManager();
                for (NFIProperty* pProperty = pConfigPropertyManager->FirstNude(strProperName); pProperty != NULL; pProperty = pConfigPropertyManager->NextNude(strProperName))
                {
                    if (pSelfPropertyManager && strProperName != NFrame::NPC::ID())
                    {
                        pSelfPropertyManager->SetProperty(pProperty->GetKey(), pProperty->GetValue());
                    }
                }
            }
        }

        if ( CLASS_OBJECT_EVENT::COE_CREATE_HASDATA == eClassEvent )
        {
            const std::string& strConfigID = m_pKernelModule->GetPropertyString(self, NFrame::NPC::ConfigID());
            int nHPMax = m_pElementInfoModule->GetPropertyInt(strConfigID, NFrame::NPC::MAXHP());
            m_pKernelModule->SetPropertyInt(self, NFrame::NPC::HP(), nHPMax);
            m_pKernelModule->AddPropertyCallBack( self, NFrame::NPC::HP(), this, &NFCNPCRefreshModule::OnObjectHPEvent );

			m_pKernelModule->AddEventCallBack( self, NFED_ON_OBJECT_BE_KILLED, this, &NFCNPCRefreshModule::OnObjectBeKilled );
        }
    }

	if ( strClassName == NFrame::Player::ThisName() && CLASS_OBJECT_EVENT::COE_CREATE_FINISH == eClassEvent )
	{
		m_pKernelModule->AddPropertyCallBack(self, NFrame::Player::Level(), this, &NFCNPCRefreshModule::OnObjectLevelEvent);
	}
    return 0;
}

int NFCNPCRefreshModule::OnObjectHPEvent( const NFGUID& self, const std::string& strPropertyName, const NFIDataList::TData& oldVar, const NFIDataList::TData& newVar)
{
    if ( newVar.GetInt() <= 0 )
    {
        NFGUID identAttacker = m_pKernelModule->GetPropertyObject( self, NFrame::NPC::LastAttacker());
        if (!identAttacker.IsNull())
		{
            m_pKernelModule->DoEvent( self, NFED_ON_OBJECT_BE_KILLED, NFCDataList() << identAttacker );

            m_pKernelModule->AddHeartBeat( self, "OnDeadDestroyHeart", this, &NFCNPCRefreshModule::OnDeadDestroyHeart, 5.0f, 1 );
        }
    }

    return 0;
}

int NFCNPCRefreshModule::OnDeadDestroyHeart( const NFGUID& self, const std::string& strHeartBeat, const float fTime, const int nCount)
{
    //and create new object
    const std::string& strClassName = m_pKernelModule->GetPropertyString( self, NFrame::NPC::ClassName());
    const std::string& strSeedID = m_pKernelModule->GetPropertyString( self, NFrame::NPC::SeedID());
    const std::string& strConfigID = m_pKernelModule->GetPropertyString( self, NFrame::NPC::ConfigID());
    int nContainerID = m_pKernelModule->GetPropertyInt( self, NFrame::NPC::SceneID());
    int nGroupID = m_pKernelModule->GetPropertyInt( self, NFrame::NPC::GroupID());

    //m_pSceneProcessModule->ClearAll( nContainerID, nGroupID, strSeendID );

	float fSeedX = m_pKernelModule->GetPropertyFloat( self, NFrame::NPC::X());
	float fSeedY = m_pKernelModule->GetPropertyFloat( self, NFrame::NPC::Y());
	float fSeedZ = m_pKernelModule->GetPropertyFloat( self, NFrame::NPC::Z());

    m_pKernelModule->DestroyObject( self );

    NFCDataList arg;
	arg << NFrame::NPC::X() << fSeedX;
    arg << NFrame::NPC::Y() << fSeedY;
    arg << NFrame::NPC::Z() << fSeedZ;
	arg << NFrame::NPC::SeedID() << strSeedID;

	m_pKernelModule->CreateObject( NFGUID(), nContainerID, nGroupID, strClassName, strConfigID, arg );

    return 0;
}

int NFCNPCRefreshModule::OnObjectBeKilled( const NFGUID& self, const int nEventID, const NFIDataList& var )
{
	if ( var.GetCount() == 1 && var.Type( 0 ) == TDATA_OBJECT )
	{
		NFGUID identKiller = var.Object( 0 );
		if ( m_pKernelModule->GetObject( identKiller ) )
		{
			const int nExp = m_pKernelModule->GetPropertyInt( self, NFrame::Player::EXP() );

			m_pLevelModule->AddExp( identKiller, nExp);

			// TODO:ӹǮ
			m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, identKiller, "Add Exp for kill monster", nExp);
		}
		else
		{
			m_pLogModule->LogObject(NFILogModule::NLL_ERROR_NORMAL, identKiller, "There is no object", __FUNCTION__, __LINE__);
		}
	}

	return 0;
}

int NFCNPCRefreshModule::OnObjectLevelEvent( const NFGUID& self, const std::string& strPropertyName, const NFIDataList::TData& oldVar, const NFIDataList::TData& newVar )
{
	NFINT64 nLevel = newVar.GetInt();

	AddLevelUpAward(self, nLevel);

	return 0;
}

bool NFCNPCRefreshModule::AddLevelUpAward( const NFGUID& self, const int nLevel )
{
	const std::string& strAwardID = NFCCommonConfig::GetSingletonPtr()->GetAttributeString("PlayerLevel", boost::lexical_cast<std::string>(nLevel), "AwardPack");
	if (strAwardID.empty())
	{
		return true;
	}

	std::vector<std::string> xList;
	NFCCommonConfig::GetSingletonPtr()->GetStructItemList(strAwardID, xList);

	for (int i = 0; i < xList.size(); ++i)
	{
		const std::string& strItemID = xList[i];
		const int nCout = NFCCommonConfig::GetSingletonPtr()->GetAttributeInt("strAwardID", strItemID, "Count");
		if (m_pElementInfoModule->ExistElement(strItemID))
		{
			const int nItemType = m_pElementInfoModule->GetPropertyInt(strItemID, NFrame::Item::ItemType());
			switch (nItemType)
			{
			case NFMsg::EIT_EQUIP:
				{
					m_pPackModule->CreateEquip(self, strItemID);
				}
				break;
			default:
				m_pPackModule->CreateItem(self, strItemID, nCout);
				break;
			}
		}
	}

	return true;
}<commit_msg>fix linux error<commit_after>// -------------------------------------------------------------------------
//    @FileName         :   NFCNPCRefreshModule.cpp
//    @Author               :    LvSheng.Huang
//    @Date                 :    2013-10-17
//    @Module               :    NFCNPCRefreshModule
//    @Desc                 :    NPCˢ
// -------------------------------------------------------------------------

#include "NFCNPCRefreshModule.h"
#include "NFComm/NFCore/NFCCommonConfig.h"

bool NFCNPCRefreshModule::Init()
{
    return true;
}

bool NFCNPCRefreshModule::Shut()
{
    return true;
}

bool NFCNPCRefreshModule::Execute()
{
    return true;
}

bool NFCNPCRefreshModule::AfterInit()
{
    m_pKernelModule = pPluginManager->FindModule<NFIKernelModule>( "NFCKernelModule" );
    m_pSceneProcessModule = pPluginManager->FindModule<NFISceneProcessModule>( "NFCSceneProcessModule" );
    m_pElementInfoModule = pPluginManager->FindModule<NFIElementInfoModule>( "NFCElementInfoModule" );
	m_pPackModule = pPluginManager->FindModule<NFIPackModule>("NFCPackModule");
	m_pLogModule = pPluginManager->FindModule<NFILogModule>("NFCLogModule");
	m_pLevelModule = pPluginManager->FindModule<NFILevelModule>("NFCLevelModule");

    assert(NULL != m_pKernelModule);
    assert(NULL != m_pSceneProcessModule);
    assert(NULL != m_pElementInfoModule);
	assert(NULL != m_pPackModule);
	assert(NULL != m_pLogModule);
	assert(NULL != m_pLevelModule);

	m_pKernelModule->AddClassCallBack(NFrame::NPC::ThisName(), this, &NFCNPCRefreshModule::OnObjectClassEvent);
	m_pKernelModule->AddClassCallBack(NFrame::Player::ThisName(), this, &NFCNPCRefreshModule::OnObjectClassEvent);

    return true;
}

int NFCNPCRefreshModule::OnObjectClassEvent( const NFGUID& self, const std::string& strClassName, const CLASS_OBJECT_EVENT eClassEvent, const NFIDataList& var )
{
    NF_SHARE_PTR<NFIObject> pSelf = m_pKernelModule->GetObject(self);
    if (nullptr == pSelf)
    {
        return 1;
    }

    if (strClassName == NFrame::NPC::ThisName())
    {
        if ( CLASS_OBJECT_EVENT::COE_CREATE_LOADDATA == eClassEvent )
        {
            const std::string& strConfigIndex = m_pKernelModule->GetPropertyString(self, NFrame::NPC::ConfigID());
            const std::string& strPropertyID = m_pElementInfoModule->GetPropertyString(strConfigIndex, NFrame::NPC::EffectData());
            NF_SHARE_PTR<NFIPropertyManager> pConfigPropertyManager = m_pElementInfoModule->GetPropertyManager(strPropertyID);
            if (pConfigPropertyManager)
            {
                std::string strProperName;
                NF_SHARE_PTR<NFIPropertyManager> pSelfPropertyManager = pSelf->GetPropertyManager();
                for (NFIProperty* pProperty = pConfigPropertyManager->FirstNude(strProperName); pProperty != NULL; pProperty = pConfigPropertyManager->NextNude(strProperName))
                {
                    if (pSelfPropertyManager && strProperName != NFrame::NPC::ID())
                    {
                        pSelfPropertyManager->SetProperty(pProperty->GetKey(), pProperty->GetValue());
                    }
                }
            }
        }

        if ( CLASS_OBJECT_EVENT::COE_CREATE_HASDATA == eClassEvent )
        {
            const std::string& strConfigID = m_pKernelModule->GetPropertyString(self, NFrame::NPC::ConfigID());
            int nHPMax = m_pElementInfoModule->GetPropertyInt(strConfigID, NFrame::NPC::MAXHP());
            m_pKernelModule->SetPropertyInt(self, NFrame::NPC::HP(), nHPMax);
            m_pKernelModule->AddPropertyCallBack( self, NFrame::NPC::HP(), this, &NFCNPCRefreshModule::OnObjectHPEvent );

			m_pKernelModule->AddEventCallBack( self, NFED_ON_OBJECT_BE_KILLED, this, &NFCNPCRefreshModule::OnObjectBeKilled );
        }
    }

	if ( strClassName == NFrame::Player::ThisName() && CLASS_OBJECT_EVENT::COE_CREATE_FINISH == eClassEvent )
	{
		m_pKernelModule->AddPropertyCallBack(self, NFrame::Player::Level(), this, &NFCNPCRefreshModule::OnObjectLevelEvent);
	}
    return 0;
}

int NFCNPCRefreshModule::OnObjectHPEvent( const NFGUID& self, const std::string& strPropertyName, const NFIDataList::TData& oldVar, const NFIDataList::TData& newVar)
{
    if ( newVar.GetInt() <= 0 )
    {
        NFGUID identAttacker = m_pKernelModule->GetPropertyObject( self, NFrame::NPC::LastAttacker());
        if (!identAttacker.IsNull())
		{
            m_pKernelModule->DoEvent( self, NFED_ON_OBJECT_BE_KILLED, NFCDataList() << identAttacker );

            m_pKernelModule->AddHeartBeat( self, "OnDeadDestroyHeart", this, &NFCNPCRefreshModule::OnDeadDestroyHeart, 5.0f, 1 );
        }
    }

    return 0;
}

int NFCNPCRefreshModule::OnDeadDestroyHeart( const NFGUID& self, const std::string& strHeartBeat, const float fTime, const int nCount)
{
    //and create new object
    const std::string& strClassName = m_pKernelModule->GetPropertyString( self, NFrame::NPC::ClassName());
    const std::string& strSeedID = m_pKernelModule->GetPropertyString( self, NFrame::NPC::SeedID());
    const std::string& strConfigID = m_pKernelModule->GetPropertyString( self, NFrame::NPC::ConfigID());
    int nContainerID = m_pKernelModule->GetPropertyInt( self, NFrame::NPC::SceneID());
    int nGroupID = m_pKernelModule->GetPropertyInt( self, NFrame::NPC::GroupID());

    //m_pSceneProcessModule->ClearAll( nContainerID, nGroupID, strSeendID );

	float fSeedX = m_pKernelModule->GetPropertyFloat( self, NFrame::NPC::X());
	float fSeedY = m_pKernelModule->GetPropertyFloat( self, NFrame::NPC::Y());
	float fSeedZ = m_pKernelModule->GetPropertyFloat( self, NFrame::NPC::Z());

    m_pKernelModule->DestroyObject( self );

    NFCDataList arg;
	arg << NFrame::NPC::X() << fSeedX;
    arg << NFrame::NPC::Y() << fSeedY;
    arg << NFrame::NPC::Z() << fSeedZ;
	arg << NFrame::NPC::SeedID() << strSeedID;

	m_pKernelModule->CreateObject( NFGUID(), nContainerID, nGroupID, strClassName, strConfigID, arg );

    return 0;
}

int NFCNPCRefreshModule::OnObjectBeKilled( const NFGUID& self, const int nEventID, const NFIDataList& var )
{
	if ( var.GetCount() == 1 && var.Type( 0 ) == TDATA_OBJECT )
	{
		NFGUID identKiller = var.Object( 0 );
		if ( m_pKernelModule->GetObject( identKiller ) )
		{
			const int nExp = m_pKernelModule->GetPropertyInt( self, NFrame::Player::EXP() );

			m_pLevelModule->AddExp( identKiller, nExp);

			// TODO:ӹǮ
			m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, identKiller, "Add Exp for kill monster", nExp);
		}
		else
		{
			m_pLogModule->LogObject(NFILogModule::NLL_ERROR_NORMAL, identKiller, "There is no object", __FUNCTION__, __LINE__);
		}
	}

	return 0;
}

int NFCNPCRefreshModule::OnObjectLevelEvent( const NFGUID& self, const std::string& strPropertyName, const NFIDataList::TData& oldVar, const NFIDataList::TData& newVar )
{
	NFINT64 nLevel = newVar.GetInt();

	AddLevelUpAward(self, nLevel);

	return 0;
}

bool NFCNPCRefreshModule::AddLevelUpAward( const NFGUID& self, const int nLevel )
{
	const std::string& strAwardID = NFCCommonConfig::GetSingletonPtr()->GetAttributeString("PlayerLevel", boost::lexical_cast<std::string>(nLevel), "AwardPack");
	if (strAwardID.empty())
	{
		return true;
	}

	std::vector<std::string> xList;
	NFCCommonConfig::GetSingletonPtr()->GetStructItemList(strAwardID, xList);

	for (int i = 0; i < xList.size(); ++i)
	{
		const std::string& strItemID = xList[i];
		const int nCout = NFCCommonConfig::GetSingletonPtr()->GetAttributeInt("strAwardID", strItemID, "Count");
		if (m_pElementInfoModule->ExistElement(strItemID))
		{
			const int nItemType = m_pElementInfoModule->GetPropertyInt(strItemID, NFrame::Item::ItemType());
			switch (nItemType)
			{
			case NFMsg::EIT_EQUIP:
				{
					m_pPackModule->CreateEquip(self, strItemID);
				}
				break;
			default:
				m_pPackModule->CreateItem(self, strItemID, nCout);
				break;
			}
		}
	}

	return true;
}
<|endoftext|>


<commit_before>/*************************************************************************/
/*  audio_stream_ogg_vorbis.cpp                                          */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* 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 "audio_stream_ogg_vorbis.h"

#include "os/file_access.h"

#include "thirdparty/misc/stb_vorbis.c"

void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {

	ERR_FAIL_COND(!active);

	int todo = p_frames;

	while (todo) {

		int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, (float *)p_buffer, todo * 2);
		todo -= mixed;
		frames_mixed += mixed;

		if (todo) {
			//end of file!
			if (vorbis_stream->loop) {
				//loop
				seek_pos(0);
				loops++;
			} else {
				for (int i = mixed; i < p_frames; i++) {
					p_buffer[i] = AudioFrame(0, 0);
				}
				active = false;
			}
		}
	}
}

float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() {

	return vorbis_stream->sample_rate;
}

void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) {

	active = true;
	seek_pos(p_from_pos);
	loops = 0;
	_begin_resample();
}

void AudioStreamPlaybackOGGVorbis::stop() {

	active = false;
}
bool AudioStreamPlaybackOGGVorbis::is_playing() const {

	return active;
}

int AudioStreamPlaybackOGGVorbis::get_loop_count() const {

	return loops;
}

float AudioStreamPlaybackOGGVorbis::get_pos() const {

	return float(frames_mixed) / vorbis_stream->sample_rate;
}
void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) {

	if (!active)
		return;

	if (p_time >= get_length()) {
		p_time = 0;
	}
	frames_mixed = uint32_t(vorbis_stream->sample_rate * p_time);

	stb_vorbis_seek(ogg_stream, frames_mixed);
}

float AudioStreamPlaybackOGGVorbis::get_length() const {

	return vorbis_stream->length;
}

AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
	if (ogg_alloc.alloc_buffer) {
		AudioServer::get_singleton()->audio_data_free(ogg_alloc.alloc_buffer);
		stb_vorbis_close(ogg_stream);
	}
}

Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {

	Ref<AudioStreamPlaybackOGGVorbis> ovs;
	printf("instance at %p, data %p\n", this, data);

	ERR_FAIL_COND_V(data == NULL, ovs);

	ovs.instance();
	ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this);
	ovs->ogg_alloc.alloc_buffer = (char *)AudioServer::get_singleton()->audio_data_alloc(decode_mem_size);
	ovs->ogg_alloc.alloc_buffer_length_in_bytes = decode_mem_size;
	ovs->frames_mixed = 0;
	ovs->active = false;
	ovs->loops = 0;
	int error;
	ovs->ogg_stream = stb_vorbis_open_memory((const unsigned char *)data, data_len, &error, &ovs->ogg_alloc);
	if (!ovs->ogg_stream) {

		AudioServer::get_singleton()->audio_data_free(ovs->ogg_alloc.alloc_buffer);
		ovs->ogg_alloc.alloc_buffer = NULL;
		ERR_FAIL_COND_V(!ovs->ogg_stream, Ref<AudioStreamPlaybackOGGVorbis>());
	}

	return ovs;
}

String AudioStreamOGGVorbis::get_stream_name() const {

	return ""; //return stream_name;
}

void AudioStreamOGGVorbis::set_data(const PoolVector<uint8_t> &p_data) {

	int src_data_len = p_data.size();
#define MAX_TEST_MEM (1 << 20)

	uint32_t alloc_try = 1024;
	PoolVector<char> alloc_mem;
	PoolVector<char>::Write w;
	stb_vorbis *ogg_stream = NULL;
	stb_vorbis_alloc ogg_alloc;

	while (alloc_try < MAX_TEST_MEM) {

		alloc_mem.resize(alloc_try);
		w = alloc_mem.write();

		ogg_alloc.alloc_buffer = w.ptr();
		ogg_alloc.alloc_buffer_length_in_bytes = alloc_try;

		PoolVector<uint8_t>::Read src_datar = p_data.read();

		int error;
		ogg_stream = stb_vorbis_open_memory((const unsigned char *)src_datar.ptr(), src_data_len, &error, &ogg_alloc);

		if (!ogg_stream && error == VORBIS_outofmem) {
			w = PoolVector<char>::Write();
			alloc_try *= 2;
		} else {

			ERR_FAIL_COND(alloc_try == MAX_TEST_MEM);
			ERR_FAIL_COND(ogg_stream == NULL);

			stb_vorbis_info info = stb_vorbis_get_info(ogg_stream);

			channels = info.channels;
			sample_rate = info.sample_rate;
			decode_mem_size = alloc_try;
			//does this work? (it's less mem..)
			//decode_mem_size = ogg_alloc.alloc_buffer_length_in_bytes + info.setup_memory_required + info.temp_memory_required + info.max_frame_size;

			//print_line("succeeded "+itos(ogg_alloc.alloc_buffer_length_in_bytes)+" setup "+itos(info.setup_memory_required)+" setup temp "+itos(info.setup_temp_memory_required)+" temp "+itos(info.temp_memory_required)+" maxframe"+itos(info.max_frame_size));

			length = stb_vorbis_stream_length_in_seconds(ogg_stream);
			stb_vorbis_close(ogg_stream);

			data = AudioServer::get_singleton()->audio_data_alloc(src_data_len, src_datar.ptr());
			data_len = src_data_len;

			break;
		}
	}

	printf("create at %p, data %p\n", this, data);
}

PoolVector<uint8_t> AudioStreamOGGVorbis::get_data() const {

	PoolVector<uint8_t> vdata;

	if (data_len && data) {
		vdata.resize(data_len);
		{
			PoolVector<uint8_t>::Write w = vdata.write();
			copymem(w.ptr(), data, data_len);
		}
	}

	return vdata;
}

void AudioStreamOGGVorbis::set_loop(bool p_enable) {
	loop = p_enable;
}

bool AudioStreamOGGVorbis::has_loop() const {

	return loop;
}

void AudioStreamOGGVorbis::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);

	ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop);
	ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop);

	ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop");
}

AudioStreamOGGVorbis::AudioStreamOGGVorbis() {

	data = NULL;
	length = 0;
	sample_rate = 1;
	channels = 1;
	decode_mem_size = 0;
	loop = false;
}
<commit_msg>Fixed AudioStreamPlaybackOGGVorbis::_mix_internal getting stuck in infinite loop causing audio to freeze<commit_after>/*************************************************************************/
/*  audio_stream_ogg_vorbis.cpp                                          */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* 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 "audio_stream_ogg_vorbis.h"

#include "os/file_access.h"

#include "thirdparty/misc/stb_vorbis.c"

void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {

	ERR_FAIL_COND(!active);

	int todo = p_frames;

	while (todo && active) {

		int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, (float *)p_buffer, todo * 2);
		todo -= mixed;
		frames_mixed += mixed;

		if (todo) {
			//end of file!
			if (vorbis_stream->loop) {
				//loop
				seek_pos(0);
				loops++;
			} else {
				for (int i = mixed; i < p_frames; i++) {
					p_buffer[i] = AudioFrame(0, 0);
				}
				active = false;
			}
		}
	}
}

float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() {

	return vorbis_stream->sample_rate;
}

void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) {

	active = true;
	seek_pos(p_from_pos);
	loops = 0;
	_begin_resample();
}

void AudioStreamPlaybackOGGVorbis::stop() {

	active = false;
}
bool AudioStreamPlaybackOGGVorbis::is_playing() const {

	return active;
}

int AudioStreamPlaybackOGGVorbis::get_loop_count() const {

	return loops;
}

float AudioStreamPlaybackOGGVorbis::get_pos() const {

	return float(frames_mixed) / vorbis_stream->sample_rate;
}
void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) {

	if (!active)
		return;

	if (p_time >= get_length()) {
		p_time = 0;
	}
	frames_mixed = uint32_t(vorbis_stream->sample_rate * p_time);

	stb_vorbis_seek(ogg_stream, frames_mixed);
}

float AudioStreamPlaybackOGGVorbis::get_length() const {

	return vorbis_stream->length;
}

AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
	if (ogg_alloc.alloc_buffer) {
		AudioServer::get_singleton()->audio_data_free(ogg_alloc.alloc_buffer);
		stb_vorbis_close(ogg_stream);
	}
}

Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {

	Ref<AudioStreamPlaybackOGGVorbis> ovs;
	printf("instance at %p, data %p\n", this, data);

	ERR_FAIL_COND_V(data == NULL, ovs);

	ovs.instance();
	ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this);
	ovs->ogg_alloc.alloc_buffer = (char *)AudioServer::get_singleton()->audio_data_alloc(decode_mem_size);
	ovs->ogg_alloc.alloc_buffer_length_in_bytes = decode_mem_size;
	ovs->frames_mixed = 0;
	ovs->active = false;
	ovs->loops = 0;
	int error;
	ovs->ogg_stream = stb_vorbis_open_memory((const unsigned char *)data, data_len, &error, &ovs->ogg_alloc);
	if (!ovs->ogg_stream) {

		AudioServer::get_singleton()->audio_data_free(ovs->ogg_alloc.alloc_buffer);
		ovs->ogg_alloc.alloc_buffer = NULL;
		ERR_FAIL_COND_V(!ovs->ogg_stream, Ref<AudioStreamPlaybackOGGVorbis>());
	}

	return ovs;
}

String AudioStreamOGGVorbis::get_stream_name() const {

	return ""; //return stream_name;
}

void AudioStreamOGGVorbis::set_data(const PoolVector<uint8_t> &p_data) {

	int src_data_len = p_data.size();
#define MAX_TEST_MEM (1 << 20)

	uint32_t alloc_try = 1024;
	PoolVector<char> alloc_mem;
	PoolVector<char>::Write w;
	stb_vorbis *ogg_stream = NULL;
	stb_vorbis_alloc ogg_alloc;

	while (alloc_try < MAX_TEST_MEM) {

		alloc_mem.resize(alloc_try);
		w = alloc_mem.write();

		ogg_alloc.alloc_buffer = w.ptr();
		ogg_alloc.alloc_buffer_length_in_bytes = alloc_try;

		PoolVector<uint8_t>::Read src_datar = p_data.read();

		int error;
		ogg_stream = stb_vorbis_open_memory((const unsigned char *)src_datar.ptr(), src_data_len, &error, &ogg_alloc);

		if (!ogg_stream && error == VORBIS_outofmem) {
			w = PoolVector<char>::Write();
			alloc_try *= 2;
		} else {

			ERR_FAIL_COND(alloc_try == MAX_TEST_MEM);
			ERR_FAIL_COND(ogg_stream == NULL);

			stb_vorbis_info info = stb_vorbis_get_info(ogg_stream);

			channels = info.channels;
			sample_rate = info.sample_rate;
			decode_mem_size = alloc_try;
			//does this work? (it's less mem..)
			//decode_mem_size = ogg_alloc.alloc_buffer_length_in_bytes + info.setup_memory_required + info.temp_memory_required + info.max_frame_size;

			//print_line("succeeded "+itos(ogg_alloc.alloc_buffer_length_in_bytes)+" setup "+itos(info.setup_memory_required)+" setup temp "+itos(info.setup_temp_memory_required)+" temp "+itos(info.temp_memory_required)+" maxframe"+itos(info.max_frame_size));

			length = stb_vorbis_stream_length_in_seconds(ogg_stream);
			stb_vorbis_close(ogg_stream);

			data = AudioServer::get_singleton()->audio_data_alloc(src_data_len, src_datar.ptr());
			data_len = src_data_len;

			break;
		}
	}

	printf("create at %p, data %p\n", this, data);
}

PoolVector<uint8_t> AudioStreamOGGVorbis::get_data() const {

	PoolVector<uint8_t> vdata;

	if (data_len && data) {
		vdata.resize(data_len);
		{
			PoolVector<uint8_t>::Write w = vdata.write();
			copymem(w.ptr(), data, data_len);
		}
	}

	return vdata;
}

void AudioStreamOGGVorbis::set_loop(bool p_enable) {
	loop = p_enable;
}

bool AudioStreamOGGVorbis::has_loop() const {

	return loop;
}

void AudioStreamOGGVorbis::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);

	ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop);
	ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop);

	ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop");
}

AudioStreamOGGVorbis::AudioStreamOGGVorbis() {

	data = NULL;
	length = 0;
	sample_rate = 1;
	channels = 1;
	decode_mem_size = 0;
	loop = false;
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Antony Arciuolo. See License.txt regarding use.
#include <oConcurrency/concurrency.h>
#include <oConcurrency/future.h>
#include <thread>

#include "../../test_services.h"

namespace ouro { namespace tests {

static void exercise_thread(size_t _Index, int* _pResults, unsigned int _RuntimeMS)
{
	int n = 0;
	auto Now = std::chrono::high_resolution_clock::now();
	auto End = Now + std::chrono::duration_cast<std::chrono::high_resolution_clock::duration>(std::chrono::milliseconds(_RuntimeMS));

	while (Now < End)
	{
		n += rand();
		Now = std::chrono::high_resolution_clock::now();
	}

	_pResults[_Index] = n;
}

static bool exercise_all_threads(ouro::test_services& services)
{
	const int nTasks = 5 * std::thread::hardware_concurrency(); // ensure more work than the number of threads.
	int* results = (int*)_alloca(nTasks * sizeof(int));
	memset(results, -1, nTasks * sizeof(int));

	parallel_for(0, size_t(nTasks), std::bind(exercise_thread, std::placeholders::_1, results, 2500));
	for (int i = 0; i < nTasks; i++)
		oTEST(results[i] != -1, "Invalid results from parallel_for");
	return true;
}

static bool fail_and_report()
{
	if (1)
		throw std::invalid_argument("not supported");
	return false;
}

static void test_workstealing(ouro::test_services& services)
{
	float CPUavg = 0.0f, CPUpeak = 0.0f;
	ouro::future<bool> Result = ouro::async(exercise_all_threads, std::ref(services));

	services.report("Waiting for result...");
	bool r = Result.get();
	oTEST(r, "future returned, but the algo returned the wrong result");

	services.get_cpu_utilization(&CPUavg, &CPUpeak);
	if (CPUpeak <= 99.0f)
	{
		float CPUpeak2 = 0.0f;
		services.reset_cpu_utilization();
		std::this_thread::sleep_for(std::chrono::seconds(10));
		services.get_cpu_utilization(&CPUavg, &CPUpeak2);
		if (CPUpeak2 > 5.0f)
			services.skip("There is too much CPU activity currently on the system to properly judge ouro::future's workstealing capabilities.");
		else
			services.error("Failed to achieve 100%c CPU utilization. Peaked at %.01f%c", '%', CPUpeak, '%');
	}
}

void TESTfuture(ouro::test_services& services)
{
	// Test packaged_task with void return type
	{
		ouro::packaged_task<void(int, int, char*)> test_no_return([&](int _Param1, int _Param2, char*_Param3){});
		test_no_return(1,2,"t");
		test_no_return.get_future().get();

		// Test if reset works
		test_no_return.reset();
		test_no_return(3,4,"t");
		test_no_return.get_future().get();
	}

	// Test packaged_task with a return type
	{
		ouro::packaged_task<int(int, const char*, int)> hmmm([&](int _Param1, const char* _Param2, int _Param3)->int{ return _Param1 + _Param3; });
		// Get future before execution
		ouro::future<int> hmmfuture = hmmm.get_future();
		hmmm(10, "a", 20);
		oTEST(hmmfuture.get() == 30, "Unexpected result1");

		// Test if reset works
		hmmm.reset();
		hmmm(20, "b", 30);
		// Get future after execution
		hmmfuture = hmmm.get_future();
		oTEST(hmmfuture.get() == 50, "Unexpected result2");
	}

	// Test swapping packaged_tasks
	{
		ouro::packaged_task<int(int,int)> tasktest1([&](int _Param1, int _Param2)->int{ return _Param1 + _Param2; });
		ouro::packaged_task<int(int,int)> tasktest2([&](int _Param1, int _Param2)->int{ return _Param1 - _Param2; });

		oTEST(tasktest1.valid() && tasktest2.valid(), "ouro::packaged_task should have been valid");

		tasktest1.swap(tasktest2);

		ouro::future<int> tasktest1_future = tasktest1.get_future();
		tasktest1(20, 10);

		tasktest2(20, 10);
		ouro::future<int> tasktest2_future = tasktest2.get_future();

		// tasktest1 should subtract
		oTEST(tasktest1_future.get() == 10, "Unexpected result3");

		// tasktest2 should add
		oTEST(tasktest2_future.get() == 30, "Unexpected result4");
	}

	// Test a packaged_task through async with maximum number of arguments
	// (std::bind is apparently limited to 10)
	{
		ouro::future<bool> Result2 = ouro::async((std::function<bool(int,int,int,int,int,int,int,int,int,int)>)[&](int _Param1,int _Param2,int _Param3,int _Param4,int _Param5,int _Param6,int _Param7,int _Param8,int _Param9,int _Param10)->bool
		{
			if (_Param10 == 10)
				return true;
			else
				return false;
		}, 1,2,3,4,5,6,7,8,9,10);

		oTEST(Result2.get(), "Unexpected result5");
	}

	// test void async()
	{
		std::atomic_int value;
		value.store(0);
		ouro::async([&] { value++; });
		test_services::timer t(services);
		while (t.seconds() < 2.0) { if (value.load() != 0) break; }
		oTEST(value != 0, "timed out waiting for void async() to finish");
	}

	// test failure
	{
		services.report("Testing graceful failure - there should be some std::system_error \"not supported\" that come through.");

		ouro::future<bool> FutureToFail = ouro::async(fail_and_report);

		bool ThisShouldFail = true;
		try { ThisShouldFail = FutureToFail.get(); }
		catch (std::system_error& e)
		{
			oTEST(e.code() == std::errc::not_supported, "error code not properly set");
			ThisShouldFail = false;
		}

		oTEST(ThisShouldFail == false, "Error reporting failed");
		services.report("Testing graceful failure - done.");
	}

	test_workstealing(services);
};

}}
<commit_msg>Fix leaks in TESTfuture.cpp<commit_after>// Copyright (c) 2014 Antony Arciuolo. See License.txt regarding use.
#include <oConcurrency/concurrency.h>
#include <oConcurrency/future.h>
#include <thread>

#include "../../test_services.h"
#include <oCore/windows/win_crt_leak_tracker.h>

namespace ouro { namespace tests {

static void exercise_thread(size_t _Index, int* _pResults, unsigned int _RuntimeMS)
{
	int n = 0;
	auto Now = std::chrono::high_resolution_clock::now();
	auto End = Now + std::chrono::duration_cast<std::chrono::high_resolution_clock::duration>(std::chrono::milliseconds(_RuntimeMS));

	while (Now < End)
	{
		n += rand();
		Now = std::chrono::high_resolution_clock::now();
	}

	_pResults[_Index] = n;
}

static bool exercise_all_threads(ouro::test_services& services)
{
	const int nTasks = 5 * std::thread::hardware_concurrency(); // ensure more work than the number of threads.
	int* results = (int*)_alloca(nTasks * sizeof(int));
	memset(results, -1, nTasks * sizeof(int));

	parallel_for(0, size_t(nTasks), std::bind(exercise_thread, std::placeholders::_1, results, 2500));
	for (int i = 0; i < nTasks; i++)
		oTEST(results[i] != -1, "Invalid results from parallel_for");
	return true;
}

static bool fail_and_report()
{
	if (1)
		throw std::invalid_argument("fail_and_report throw, this should have been caught");
	return false;
}

static void test_workstealing(ouro::test_services& services)
{
	float CPUavg = 0.0f, CPUpeak = 0.0f;
	ouro::future<bool> Result = ouro::async(exercise_all_threads, std::ref(services));

	services.report("Waiting for result...");
	bool r = Result.get();
	oTEST(r, "future returned, but the algo returned the wrong result");

	services.get_cpu_utilization(&CPUavg, &CPUpeak);
	if (CPUpeak <= 99.0f)
	{
		float CPUpeak2 = 0.0f;
		services.reset_cpu_utilization();
		std::this_thread::sleep_for(std::chrono::seconds(10));
		services.get_cpu_utilization(&CPUavg, &CPUpeak2);
		if (CPUpeak2 > 5.0f)
			services.skip("There is too much CPU activity currently on the system to properly judge ouro::future's workstealing capabilities.");
		else
			services.error("Failed to achieve 100%c CPU utilization. Peaked at %.01f%c", '%', CPUpeak, '%');
	}
	else
		services.report("");
}

void TESTfuture(ouro::test_services& services)
{
	{
		ouro::packaged_task<void(int, int, char*)> test_no_return([&](int _Param1, int _Param2, char*_Param3){});
		test_no_return(1,2,"t");
		test_no_return.get_future().get();

		// Test if reset works
		test_no_return.reset();
		test_no_return(3,4,"t");
		test_no_return.get_future().get();
	}

	// Test packaged_task with a return type
	{
		ouro::packaged_task<int(int, const char*, int)> hmmm([&](int _Param1, const char* _Param2, int _Param3)->int{ return _Param1 + _Param3; });
		// Get future before execution
		ouro::future<int> hmmfuture = hmmm.get_future();
		hmmm(10, "a", 20);
		oTEST(hmmfuture.get() == 30, "Unexpected result1");

		// Test if reset works
		hmmm.reset();
		hmmm(20, "b", 30);
		// Get future after execution
		hmmfuture = hmmm.get_future();
		oTEST(hmmfuture.get() == 50, "Unexpected result2");
	}

	// Test swapping packaged_tasks
	{
		ouro::packaged_task<int(int,int)> tasktest1([&](int _Param1, int _Param2)->int{ return _Param1 + _Param2; });
		ouro::packaged_task<int(int,int)> tasktest2([&](int _Param1, int _Param2)->int{ return _Param1 - _Param2; });

		oTEST(tasktest1.valid() && tasktest2.valid(), "ouro::packaged_task should have been valid");

		tasktest1.swap(tasktest2);

		ouro::future<int> tasktest1_future = tasktest1.get_future();
		tasktest1(20, 10);

		tasktest2(20, 10);
		ouro::future<int> tasktest2_future = tasktest2.get_future();

		// tasktest1 should subtract
		oTEST(tasktest1_future.get() == 10, "Unexpected result3");

		// tasktest2 should add
		oTEST(tasktest2_future.get() == 30, "Unexpected result4");
	}

	// Test a packaged_task through async with maximum number of arguments
	// (std::bind is apparently limited to 10)
	{
		ouro::future<bool> Result2 = ouro::async((std::function<bool(int,int,int,int,int,int,int,int,int,int)>)[&](int _Param1,int _Param2,int _Param3,int _Param4,int _Param5,int _Param6,int _Param7,int _Param8,int _Param9,int _Param10)->bool
		{
			if (_Param10 == 10)
				return true;
			else
				return false;
		}, 1,2,3,4,5,6,7,8,9,10);

		oTEST(Result2.get(), "Unexpected result5");
	}

	// test void async()
	{
		std::atomic_int value;
		value.store(0);
		ouro::future<void> Result3 = ouro::async([&] { value++; });
		test_services::timer t(services);
		while (t.seconds() < 2.0) { if (value.load() != 0) break; }
		oTEST(value != 0, "timed out waiting for void async() to finish");

		// @tony: the standard async always requires a return future. Calling get()
		// in a future's dtor seems bad since it would turn things into a sync call.
		// Still it seems there should be a way to fire-and-forget (or maybe there
		// shouldn't?) so swing back to ouro::async sometime and try to get a void
		// version.
		Result3.get();
	}

	// test failure
	{
		services.report("Testing graceful failure - there should be some std::system_error \"not supported\" that come through.");

		ouro::future<bool> FutureToFail = ouro::async(fail_and_report);
		bool ThisShouldFail = true;

		try { ThisShouldFail = FutureToFail.get(); }
		catch (std::invalid_argument&)
		{
			ThisShouldFail = false;
		}

		oTEST(!ThisShouldFail, "Error reporting failed");
		services.report("Testing graceful failure - done.");
	}

	test_workstealing(services);
};

}}
<|endoftext|>


<commit_before><commit_msg>Debug?<commit_after><|endoftext|>


<commit_before>#include "MeshUtility.h"

using namespace Rendering;
using namespace Rendering::Geometry;
using namespace Intersection;
using namespace Rendering::Manager;
using namespace Core;
using namespace Math;

void Geometry::MeshUtility::Culling(const Frustum& frustum, MeshManager& meshMgr, TransformPool& transformPool)
{
	auto Cull = [&frustum, &meshMgr, &transformPool](auto trait) -> void
	{
		auto& pool = meshMgr.GetPool<decltype(trait)>();
		uint size = meshMgr.GetPool<decltype(trait)>().GetSize();
		for (uint meshIdx = 0; meshIdx < size; ++meshIdx)
		{
			auto& mesh = pool.Get(meshIdx);

			ObjectId id = mesh.GetObjectId();
			Transform* transform = transformPool.Find(id);
			Vector3 worldPos = transform->GetWorldPosition();

			mesh._culled = frustum.In(worldPos, mesh.GetRadius());
		}
	};

	Cull(OpaqueTrait());
	Cull(TransparencyTrait());
	Cull(AlphaBlendTrait());
}<commit_msg>MeshUtility.cpp - Culling 수정<commit_after>#include "MeshUtility.h"

using namespace Rendering;
using namespace Rendering::Geometry;
using namespace Intersection;
using namespace Rendering::Manager;
using namespace Core;
using namespace Math;

void Geometry::MeshUtility::Culling(const Frustum& frustum, MeshManager& meshMgr, TransformPool& transformPool)
{
	auto Cull = [&frustum, &meshMgr, &transformPool](auto& pool) -> void
	{
		uint size = pool.GetSize();
		for (uint meshIdx = 0; meshIdx < size; ++meshIdx)
		{
			auto& mesh = pool.Get(meshIdx);

			ObjectId id = mesh.GetObjectId();
			Transform* transform = transformPool.Find(id);
			Vector3 worldPos = transform->GetWorldPosition();

			mesh._culled = frustum.In(worldPos, mesh.GetRadius());
		}
	};

	Cull( meshMgr.GetPool<OpaqueTrait>() );
	Cull( meshMgr.GetPool<TransparencyTrait>() );
	Cull( meshMgr.GetPool<AlphaBlendTrait>() );
}
<|endoftext|>


<commit_before>
#include "LearningWindowManager.h"
#include "ShaderProgram.h"
#include "Scene.h"
#include "DistributedShapesScene.h"
#include "PongScene.h"
#include "LyingShapesScene.h"
#include "SPHScene.h"

#include <sstream>
#include <glm/gtc/type_ptr.hpp>

LearningWindowManager::LearningWindowManager( const std::string& configFile )
	: WindowManager( configFile ) ,
	camera({ 1024.0f,768.0f }, Camera::ProjectionType::PERSPECTIVE, { 1.0f,1000.0f }),
	averageFrameTime( 60 )
{
	//setProjection( 2.0, 2.0 );
	//view = glm::mat4(1.0f);
	//view = glm::lookAt( glm::vec3(0,0,10), glm::vec3(0,0,0), glm::vec3(0,1,0) );
}

void LearningWindowManager::open()
{
	LearningWindowManager window("data/windowSettings.txt");
	window.run();
}

void LearningWindowManager::addScene( Scene* scene )
{
	scenes.push_back( scene );
	auto size = window->getSize();
	scene->eventReshape( size.x, size.y );
}

void LearningWindowManager::windowWillClose()
{
	for( auto sceneIt = scenes.begin(); sceneIt != scenes.end(); sceneIt++ )
	{
		delete (*sceneIt);
	}

}

void LearningWindowManager::windowWillRun()
{
	auto size = window->getSize();
	eventReshape( size.x, size.y );

	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);
}

void LearningWindowManager::eventReshape( int width, int height)
{
	setProjection( width, height );
	camera.windowDidResize(width, height);
	for( unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->eventReshape(width, height);
	}
}

void LearningWindowManager::eventKeyboardDown( sf::Keyboard::Key keyPressed )
{
	for( unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->eventKeyboardDown( keyPressed );
	}

	handleCameraMove(keyPressed, true);
}

void LearningWindowManager::eventKeyboardUp( sf::Keyboard::Key keyPressed )
{
	for( unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->eventKeyboardUp( keyPressed );
	}

	if( keyPressed == sf::Keyboard::Num1 )
	{
		addScene( new DistributedShapesScene() );
	}
	if ( keyPressed == sf::Keyboard::Num2 )
	{
		addScene( new PongScene() );
	}
	if (keyPressed == sf::Keyboard::Num3)
	{
		addScene(new LyingShapesScene());
	}
	if (keyPressed == sf::Keyboard::Num4)
	{
		addScene(new SPHScene());
	}
	if( keyPressed == sf::Keyboard::Num0 )
	{
		if( scenes.size() > 0 )
		{
			auto lastOne = scenes.end() - 1;
			delete *lastOne;
			scenes.erase( lastOne );
		}
	}
	handleCameraMove(keyPressed, false);
}

void LearningWindowManager::eventMouseMotion(sf::Event::MouseMoveEvent mEvent)
{
	int dx = mEvent.x - mouseLastX;
	int dy = mEvent.y - mouseLastY;
	//std::cout << dx << "  " << dy << std::endl;

	if (mouseLeft) {
		camera.rotate(dx, dy);
	}

	WindowManager::eventMouseMotion(mEvent);
}

void LearningWindowManager::handleCameraMove(sf::Keyboard::Key key, bool pressed) {
	if (key == sf::Keyboard::Left) {
		camera.setMoveLeft(pressed);
	}
	if (key == sf::Keyboard::Right) {
		camera.setMoveRight(pressed);
	}
	if (key == sf::Keyboard::Up) {
		camera.setMoveForward(pressed);
	}
	if (key == sf::Keyboard::Down) {
		camera.setMoveBackward(pressed);
	}
}

void LearningWindowManager::setProjection( double width, double height )
{
	std::cout << "Reshape projection: " << width << ", " << height << std::endl;
	//camera.setupOtrhographicProjection({ 0.0f, 0.0f }, { (float)width, (float)height }, 0.0f, 100.0f);
	//
	//double orthoHeight = height / 3;
	//double orthoWidth = orthoHeight * width / height;
	//
	////projection = glm::ortho( orthoWidth*-0.5, orthoWidth*0.5, orthoHeight*-0.5, orthoHeight*0.5 );
	//projection = glm::ortho(0.0, width, 0.0, height,0.0,100.0 );
	////projection = glm::ortho(-100.0, 100.0, -100.0, 100.0 );

	////view = glm::translate( glm::mat4(), glm::vec3(-orthoWidth * 0.5, -orthoHeight * 0.5,0.0) );
}

void LearningWindowManager::drawGLScene()
{
	//auto viewProjection = camera.getViewProjection(); // projection * view;

	camera.loadViewport();
	for( unsigned int i = 0; i<scenes.size(); i++ )
	{
		scenes[i]->draw( camera );
	}

	WindowManager::drawGLScene();
}

void LearningWindowManager::drawSFMLScene(sf::RenderWindow & window)
{
	for (unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->drawSFML(window);
	}

	WindowManager::drawSFMLScene(window);
}

void LearningWindowManager::updateScene( double dt )
{
	camera.update(dt);
	camera.updateCamera();

	for (unsigned int i = 0; i < scenes.size(); i++)
	{
		scenes[i]->update(dt);
	}

	averageFrameTime.addValue(1, dt);
	double perFrame = averageFrameTime.currentAverage();
	std::stringstream titleStream;
	titleStream << "Time: " << perFrame << " FPS: " << 1.0/perFrame;
	std::string title = titleStream.str();
	window->setTitle( title.c_str() );
}
<commit_msg>SPHScene shows on the beginning.<commit_after>
#include "LearningWindowManager.h"
#include "ShaderProgram.h"
#include "Scene.h"
#include "DistributedShapesScene.h"
#include "PongScene.h"
#include "LyingShapesScene.h"
#include "SPHScene.h"

#include <sstream>
#include <glm/gtc/type_ptr.hpp>

LearningWindowManager::LearningWindowManager( const std::string& configFile )
	: WindowManager( configFile ) ,
	camera({ 1024.0f,768.0f }, Camera::ProjectionType::PERSPECTIVE, { 1.0f,1000.0f }),
	averageFrameTime( 60 )
{

}

void LearningWindowManager::open()
{
	LearningWindowManager window("data/windowSettings.txt");
	window.run();
}

void LearningWindowManager::addScene( Scene* scene )
{
	scenes.push_back( scene );
	auto size = window->getSize();
	scene->eventReshape( size.x, size.y );
}

void LearningWindowManager::windowWillClose()
{
	for( auto sceneIt = scenes.begin(); sceneIt != scenes.end(); sceneIt++ )
	{
		delete (*sceneIt);
	}

}

void LearningWindowManager::windowWillRun()
{
	auto size = window->getSize();
	eventReshape( size.x, size.y );

	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);

	addScene(new SPHScene());
}

void LearningWindowManager::eventReshape( int width, int height)
{
	setProjection( width, height );
	camera.windowDidResize(width, height);
	for( unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->eventReshape(width, height);
	}
}

void LearningWindowManager::eventKeyboardDown( sf::Keyboard::Key keyPressed )
{
	for( unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->eventKeyboardDown( keyPressed );
	}

	handleCameraMove(keyPressed, true);
}

void LearningWindowManager::eventKeyboardUp( sf::Keyboard::Key keyPressed )
{
	for( unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->eventKeyboardUp( keyPressed );
	}

	if( keyPressed == sf::Keyboard::Num1 )
	{
		addScene( new DistributedShapesScene() );
	}
	if ( keyPressed == sf::Keyboard::Num2 )
	{
		addScene( new PongScene() );
	}
	if (keyPressed == sf::Keyboard::Num3)
	{
		addScene(new LyingShapesScene());
	}
	if (keyPressed == sf::Keyboard::Num4)
	{
		addScene(new SPHScene());
	}
	if( keyPressed == sf::Keyboard::Num0 )
	{
		if( scenes.size() > 0 )
		{
			auto lastOne = scenes.end() - 1;
			delete *lastOne;
			scenes.erase( lastOne );
		}
	}
	handleCameraMove(keyPressed, false);
}

void LearningWindowManager::eventMouseMotion(sf::Event::MouseMoveEvent mEvent)
{
	int dx = mEvent.x - mouseLastX;
	int dy = mEvent.y - mouseLastY;
	//std::cout << dx << "  " << dy << std::endl;

	if (mouseLeft) {
		camera.rotate(dx, dy);
	}

	WindowManager::eventMouseMotion(mEvent);
}

void LearningWindowManager::handleCameraMove(sf::Keyboard::Key key, bool pressed) {
	if (key == sf::Keyboard::Left) {
		camera.setMoveLeft(pressed);
	}
	if (key == sf::Keyboard::Right) {
		camera.setMoveRight(pressed);
	}
	if (key == sf::Keyboard::Up) {
		camera.setMoveForward(pressed);
	}
	if (key == sf::Keyboard::Down) {
		camera.setMoveBackward(pressed);
	}
}

void LearningWindowManager::setProjection( double width, double height )
{
	std::cout << "Reshape projection: " << width << ", " << height << std::endl;
	//camera.setupOtrhographicProjection({ 0.0f, 0.0f }, { (float)width, (float)height }, 0.0f, 100.0f);
	//
	//double orthoHeight = height / 3;
	//double orthoWidth = orthoHeight * width / height;
	//
	////projection = glm::ortho( orthoWidth*-0.5, orthoWidth*0.5, orthoHeight*-0.5, orthoHeight*0.5 );
	//projection = glm::ortho(0.0, width, 0.0, height,0.0,100.0 );
	////projection = glm::ortho(-100.0, 100.0, -100.0, 100.0 );

	////view = glm::translate( glm::mat4(), glm::vec3(-orthoWidth * 0.5, -orthoHeight * 0.5,0.0) );
}

void LearningWindowManager::drawGLScene()
{
	//auto viewProjection = camera.getViewProjection(); // projection * view;

	camera.loadViewport();
	for( unsigned int i = 0; i<scenes.size(); i++ )
	{
		scenes[i]->draw( camera );
	}

	WindowManager::drawGLScene();
}

void LearningWindowManager::drawSFMLScene(sf::RenderWindow & window)
{
	for (unsigned int i = 0; i<scenes.size(); i++)
	{
		scenes[i]->drawSFML(window);
	}

	WindowManager::drawSFMLScene(window);
}

void LearningWindowManager::updateScene( double dt )
{
	camera.update(dt);
	camera.updateCamera();

	for (unsigned int i = 0; i < scenes.size(); i++)
	{
		scenes[i]->update(dt);
	}

	averageFrameTime.addValue(1, dt);
	double perFrame = averageFrameTime.currentAverage();
	std::stringstream titleStream;
	titleStream << "Time: " << perFrame << " FPS: " << 1.0/perFrame;
	std::string title = titleStream.str();
	window->setTitle( title.c_str() );
}
<|endoftext|>


<commit_before>/// HEADER
#include <csapex/utility/slim_signal.h>

/// PROJECT
#include <csapex/utility/assert.h>
#include <csapex/utility/exceptions.h>

/// SYSTEM
#include <algorithm>
#include <iostream>

using namespace csapex;
using namespace slim_signal;

SignalBase::SignalBase()
    : guard_(-1)
{

}

SignalBase::~SignalBase()
{
    std::unique_lock<std::recursive_mutex> lock(mutex_);

    while(!connections_.empty()) {
        Connection* c = connections_.front();
//        apex_assert_hard(c->parent_ == this);
        if(c->isDetached()) {
            connections_.erase(connections_.begin());
        } else {
            c->detach();
        }
    }

    guard_ = 0xDEADBEEF;
}

void SignalBase::addConnection(Connection *connection)
{
    apex_assert_hard(connection->parent_ == this);
    apex_assert_hard(guard_ == -1);

    std::unique_lock<std::recursive_mutex> lock(mutex_);

    connections_.push_back(connection);
}

void SignalBase::removeConnection(const Connection *connection)
{
    apex_assert_hard(connection->parent_ == this);
    apex_assert_hard(guard_ == -1);

    std::unique_lock<std::recursive_mutex> lock(mutex_);

    for(auto it = connections_.begin(); it != connections_.end();) {
        if(*it == connection) {
            it = connections_.erase(it);
        } else {
            ++it;
        }
    }
}

bool SignalBase::isConnected() const
{
    return !connections_.empty();
}

void SignalBase::disconnectAll()
{
    apex_assert_hard(guard_ == -1);
    std::unique_lock<std::recursive_mutex> lock(mutex_);

    for(Connection* c : connections_) {
        c->disconnect();
    }
    connections_.clear();

    lock.unlock();
    onDisconnect();
    lock.lock();
}

void SignalBase::onConnect()
{
}

void SignalBase::onDisconnect()
{
}

Connection::Connection(SignalBase* parent, const Deleter& del, SignalBase *child)
    : parent_(parent), deleter_(del), child_(child)
{
    apex_assert_hard(parent);
    parent_->addConnection(this);
}

Connection::Connection(const Connection& other)
    : parent_(other.parent_), deleter_(other.deleter_), child_(other.child_)
{
    if(parent_) {
        apex_assert_hard(parent_->guard_ == -1);
        parent_->addConnection(this);
    }
}

Connection::Connection()
    : parent_(nullptr), child_(nullptr)
{
}

Connection::~Connection()
{
    if(parent_) {
        detach();
    }
}

void Connection::detach() const
{
    if(!detached_) {
        detached_ = true;
        parent_->removeConnection(this);
        parent_ = nullptr;
    }
}

bool Connection::isDetached() const
{
    return detached_;
}

SignalBase* Connection::getParent() const
{
    return parent_;
}

SignalBase* Connection::getChild() const
{
    return child_;
}

void Connection::disconnect() const
{
    if(parent_) {
        apex_assert_hard(parent_->guard_ == -1);
        if(!isDetached()) {
            detach();
            if(deleter_) {
                deleter_();
            }
        }
    }
}

ScopedConnection::ScopedConnection(const Connection& c)
    : Connection(c)
{
}
ScopedConnection::ScopedConnection(ScopedConnection&& c) noexcept
    : Connection(c)
{
    if(c.parent_) {
        c.parent_->removeConnection(&c);
        c.parent_ = nullptr;
    }
}
ScopedConnection::ScopedConnection()
{
}

ScopedConnection::~ScopedConnection()
{
    if(parent_) {
        try {
            disconnect();
        } catch(const csapex::Failure& e) {
            std::cerr << "Failure in ~ScopedConnection: " << e.what() << std::endl;
        }
    }
}

void ScopedConnection::operator = (const Connection& c)
{
    apex_assert_hard(c.parent_ != nullptr);
    disconnect();
    deleter_ = c.deleter_;
    parent_ = c.parent_;
    parent_->addConnection(this);
}

void ScopedConnection::operator = (ScopedConnection&& c) noexcept
{
    apex_assert_hard(c.parent_ != nullptr);
    disconnect();
    deleter_ = c.deleter_;
    parent_ = c.parent_;
    c.parent_->removeConnection(&c);
    parent_->addConnection(this);

    c.parent_ = nullptr;
}
<commit_msg>Change invalid assertions in slim signal<commit_after>/// HEADER
#include <csapex/utility/slim_signal.h>

/// PROJECT
#include <csapex/utility/assert.h>
#include <csapex/utility/exceptions.h>

/// SYSTEM
#include <algorithm>
#include <iostream>

using namespace csapex;
using namespace slim_signal;

SignalBase::SignalBase()
    : guard_(-1)
{

}

SignalBase::~SignalBase()
{
    std::unique_lock<std::recursive_mutex> lock(mutex_);

    while(!connections_.empty()) {
        Connection* c = connections_.front();
//        apex_assert_hard(c->parent_ == this);
        if(c->isDetached()) {
            connections_.erase(connections_.begin());
        } else {
            c->detach();
        }
    }

    guard_ = 0xDEADBEEF;
}

void SignalBase::addConnection(Connection *connection)
{
    apex_assert_hard(connection->parent_ == this);
    apex_assert_hard(guard_ == -1);

    std::unique_lock<std::recursive_mutex> lock(mutex_);

    connections_.push_back(connection);
}

void SignalBase::removeConnection(const Connection *connection)
{
    apex_assert_hard(guard_ == -1);

    std::unique_lock<std::recursive_mutex> lock(mutex_);

    for(auto it = connections_.begin(); it != connections_.end();) {
        if(*it == connection) {
            it = connections_.erase(it);
        } else {
            ++it;
        }
    }
}

bool SignalBase::isConnected() const
{
    return !connections_.empty();
}

void SignalBase::disconnectAll()
{
    apex_assert_hard(guard_ == -1);
    std::unique_lock<std::recursive_mutex> lock(mutex_);

    for(Connection* c : connections_) {
        c->disconnect();
    }
    connections_.clear();

    lock.unlock();
    onDisconnect();
    lock.lock();
}

void SignalBase::onConnect()
{
}

void SignalBase::onDisconnect()
{
}

Connection::Connection(SignalBase* parent, const Deleter& del, SignalBase *child)
    : parent_(parent), deleter_(del), child_(child)
{
    apex_assert_hard(parent);
    parent_->addConnection(this);
}

Connection::Connection(const Connection& other)
    : parent_(other.parent_), deleter_(other.deleter_), child_(other.child_)
{
    if(parent_) {
        apex_assert_hard(parent_->guard_ == -1);
        parent_->addConnection(this);
    }
}

Connection::Connection()
    : parent_(nullptr), child_(nullptr)
{
}

Connection::~Connection()
{
    if(parent_) {
        detach();
    }
}

void Connection::detach() const
{
    if(!detached_) {
        detached_ = true;
        auto* tmp = parent_;
        parent_ = nullptr;
        tmp->removeConnection(this);
    }
}

bool Connection::isDetached() const
{
    return detached_;
}

SignalBase* Connection::getParent() const
{
    return parent_;
}

SignalBase* Connection::getChild() const
{
    return child_;
}

void Connection::disconnect() const
{
    if(parent_) {
        if(!isDetached()) {
            apex_assert_hard(parent_->guard_ == -1);
            detach();
            if(deleter_) {
                deleter_();
            }
        }
    }
}

ScopedConnection::ScopedConnection(const Connection& c)
    : Connection(c)
{
}
ScopedConnection::ScopedConnection(ScopedConnection&& c) noexcept
    : Connection(c)
{
    if(c.parent_) {
        c.parent_->removeConnection(&c);
        c.parent_ = nullptr;
    }
}
ScopedConnection::ScopedConnection()
{
}

ScopedConnection::~ScopedConnection()
{
    if(parent_) {
        try {
            disconnect();
        } catch(const csapex::Failure& e) {
            std::cerr << "Failure in ~ScopedConnection: " << e.what() << std::endl;
        }
    }
}

void ScopedConnection::operator = (const Connection& c)
{
    apex_assert_hard(c.parent_ != nullptr);
    disconnect();
    deleter_ = c.deleter_;
    parent_ = c.parent_;
    parent_->addConnection(this);
}

void ScopedConnection::operator = (ScopedConnection&& c) noexcept
{
    apex_assert_hard(c.parent_ != nullptr);
    disconnect();
    deleter_ = c.deleter_;
    parent_ = c.parent_;
    c.parent_->removeConnection(&c);
    parent_->addConnection(this);

    c.parent_ = nullptr;
}
<|endoftext|>