<commit_before>/*
  Adplug - Replayer for many OPL2/OPL3 audio file formats.
  Copyright (C) 1999 - 2004, 2006 Simon Peter, <dn.tlp@gmx.net>, et al.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

  dmo.cpp - TwinTeam loader by Riven the Mage <riven@ok.ru>
*/
/*
  NOTES:
  Panning is ignored.

  A WORD ist 16 bits, a DWORD is 32 bits and a BYTE is 8 bits in this context.
*/

#include <string.h>
#include <binstr.h>

#include "dmo.h"
#include "debug.h"

#define ARRAY_AS_DWORD(a, i) \
((a[i + 3] << 24) + (a[i + 2] << 16) + (a[i + 1] << 8) + a[i])
#define ARRAY_AS_WORD(a, i)	((a[i + 1] << 8) + a[i])

/* -------- Public Methods -------------------------------- */

CPlayer *CdmoLoader::factory(Copl *newopl)
{
  return new CdmoLoader(newopl);
}

bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp)
{
  int i, j;

  binistream *f = fp.open(filename);
  if (!f) return false;

  // check header
  dmo_unpacker unpacker;
  unsigned char chkhdr[16];

  f->readString((char *)chkhdr, 16);

  if (!unpacker.decrypt(chkhdr, 16))
    {
      fp.close(f);
      return false;
    }

  // get file size
  long packed_length = fp.filesize(f);
  f->seek(0);

  unsigned char *packed_module = new unsigned char [packed_length];

  // load file
  f->readString((char *)packed_module, packed_length);
  fp.close(f);

  // decrypt
  unpacker.decrypt(packed_module, packed_length);

  long unpacked_length = 0x2000 * ARRAY_AS_WORD(packed_module, 12);
  unsigned char *module = new unsigned char [unpacked_length];

  // unpack
  if (!unpacker.unpack(packed_module, packed_length, module, unpacked_length))
    {
      delete [] packed_module;
      delete [] module;
      return false;
    }

  delete [] packed_module;

  // "TwinTeam" - signed ?
  if (memcmp(module,"TwinTeam Module File""\x0D\x0A",22))
    {
      delete [] module;
      return false;
    }

  // load header
  binisstream	uf(module, unpacked_length);
  uf.setFlag(binio::BigEndian, false); uf.setFlag(binio::FloatIEEE);

  memset(&header,0,sizeof(s3mheader));

  uf.ignore(22);				// ignore DMO header ID string
  uf.readString(header.name, 28);

  uf.ignore(2);				// _unk_1
  header.ordnum  = uf.readInt(2);
  header.insnum  = uf.readInt(2);
  header.patnum  = uf.readInt(2);
  uf.ignore(2);				// _unk_2
  header.is      = uf.readInt(2);
  header.it      = uf.readInt(2);

  memset(header.chanset,0xFF,32);

  for (i=0;i<9;i++)
    header.chanset[i] = 0x10 + i;

  uf.ignore(32);				// ignore panning settings for all 32 channels

  // load orders
  for(i = 0; i < 256; i++) orders[i] = uf.readInt(1);

  orders[header.ordnum] = 0xFF;

  // load pattern lengths
  unsigned short my_patlen[100];
  for(i = 0; i < 100; i++) my_patlen[i] = uf.readInt(2);

  // load instruments
  for (i = 0; i < header.insnum; i++)
    {
      memset(&inst[i],0,sizeof(s3minst));

      uf.readString(inst[i].name, 28);

      inst[i].volume = uf.readInt(1);
      inst[i].dsk    = uf.readInt(1);
      inst[i].c2spd  = uf.readInt(4);
      inst[i].type   = uf.readInt(1);
      inst[i].d00    = uf.readInt(1);
      inst[i].d01    = uf.readInt(1);
      inst[i].d02    = uf.readInt(1);
      inst[i].d03    = uf.readInt(1);
      inst[i].d04    = uf.readInt(1);
      inst[i].d05    = uf.readInt(1);
      inst[i].d06    = uf.readInt(1);
      inst[i].d07    = uf.readInt(1);
      inst[i].d08    = uf.readInt(1);
      inst[i].d09    = uf.readInt(1);
      inst[i].d0a    = uf.readInt(1);
      /*
       * Originally, riven sets d0b = d0a and ignores 1 byte in the
       * stream, but i guess this was a typo, so i read it here.
       */
      inst[i].d0b    = uf.readInt(1);
    }

  // load patterns
  for (i = 0; i < header.patnum; i++) {
    long cur_pos = uf.pos();

    for (j = 0; j < 64; j++) {
      while (1) {
	unsigned char token = uf.readInt(1);

	if (!token)
	  break;

	unsigned char chan = token & 31;

	// note + instrument ?
	if (token & 32) {
	  unsigned char bufbyte = uf.readInt(1);

	  pattern[i][j][chan].note = bufbyte & 15;
	  pattern[i][j][chan].oct = bufbyte >> 4;
	  pattern[i][j][chan].instrument = uf.readInt(1);
	}

	// volume ?
	if (token & 64)
	  pattern[i][j][chan].volume = uf.readInt(1);

	// command ?
	if (token & 128) {
	  pattern[i][j][chan].command = uf.readInt(1);
	  pattern[i][j][chan].info = uf.readInt(1);
	}
      }
    }

    uf.seek(cur_pos + my_patlen[i]);
  }

  delete [] module;
  rewind(0);
  return true;
}

std::string CdmoLoader::gettype()
{
  return std::string("TwinTeam (packed S3M)");
}

std::string CdmoLoader::getauthor()
{
  /*
    All available .DMO modules written by one composer. And because all .DMO
    stuff was lost due to hd crash (TwinTeam guys said this), there are
    never(?) be another.
  */
  return std::string("Benjamin GERARDIN");
}

/* -------- Private Methods ------------------------------- */

unsigned short CdmoLoader::dmo_unpacker::brand(unsigned short range)
{
  bseed *= 0x08088405U;
  bseed++;

  return (uint64_t)bseed * range >> 32;
}

bool CdmoLoader::dmo_unpacker::decrypt(unsigned char *buf, size_t len)
{
  if (len < headersize)
    return false;

  bseed = ARRAY_AS_DWORD(buf, 0);

  uint32_t seed = 0;
  for (int i = 0; i <= ARRAY_AS_WORD(buf, 4); i++)
    seed += brand(0xffff);

  bseed = seed ^ ARRAY_AS_DWORD(buf, 6);

  if (ARRAY_AS_WORD(buf, 10) != brand(0xffff))
    return false;

  for (size_t i = headersize; i < len; i++)
    buf[i] ^= brand(0x100);

  buf[len - 2] = buf[len - 1] = 0;

  return true;
}

long CdmoLoader::dmo_unpacker::unpack_block(unsigned char *ibuf, size_t ilen,
					    unsigned char *obuf, size_t olen)
{
  size_t ipos = 0, opos = 0;

  // LZ77 child
  while (ipos < ilen) {
    size_t cpy = 0, ofs = 0, lit = 0;
    unsigned char code, par1, par2;

    code = ibuf[ipos++];
    par1 = ipos < ilen ? ibuf[ipos] : 0;
    par2 = ipos + 1 < ilen ? ibuf[ipos + 1] : 0;
    switch (code >> 6) {
    case 0:
      // 00xxxxxx: use (X + 1) literal bytes
      lit = (code & 0x3F) + 1;
      break;

    case 1:
      // 01xxxxxx xxxyyyyy: copy (Y + 3) bytes from offset (X + 1)
      ipos++; // for par1
      ofs = ((code & 0x3F) << 3) + ((par1 & 0xE0) >> 5) + 1;
      cpy = (par1 & 0x1F) + 3;
      break;

    case 2:
      // 10xxxxxx xyyyzzzz: copy (Y + 3) bytes from offset(X + 1);
      // use Z literal bytes
      ipos++; // for par1
      ofs = ((code & 0x3F) << 1) + (par1 >> 7) + 1;
      cpy = ((par1 & 0x70) >> 4) + 3;
      lit = par1 & 0x0F;
      break;

    case 3:
      // 11xxxxxx xxxxxxxy yyyyzzzz: copy (Y + 4) from offset X;
      // use Z literal bytes
      ipos += 2; // for par1 and par2
      ofs = ((code & 0x3F) << 7) + (par1 >> 1);
      cpy = ((par1 & 0x01) << 4) + (par2 >> 4) + 4;
      lit = par2 & 0x0F;
      break;
    }

    // check lengths and offset
    if (ipos + lit > ilen || opos + cpy + lit > olen || ofs > opos)
      return -1;

    // copy - can't use memcpy() because source and destination may overlap
    for (size_t i = 0; i < cpy; i++)
      obuf[opos + i] = obuf[opos - ofs + i];
    opos += cpy;

    // copy literal bytes
    while (lit--)
      obuf[opos++] = ibuf[ipos++];
  }

  return opos;
}

size_t CdmoLoader::dmo_unpacker::unpack(unsigned char *ibuf, size_t inputsize,
					unsigned char *obuf, size_t outputsize)
{
  if (inputsize < headersize + 2)
    return 0;

  unsigned short block_count = ARRAY_AS_WORD(ibuf, headersize);
  unsigned block_start = headersize + 2 + 2 * block_count;

  if (inputsize < block_start)
    return 0;

  unsigned char *block_length = &ibuf[headersize + 2];
  ibuf += block_start;
  inputsize -= block_start;

  size_t olen = 0;
  for (int i = 0; i < block_count; i++) {
    unsigned short blen = ARRAY_AS_WORD(block_length, 2 * i);
    if (blen < 2 || blen > inputsize)
      return 0;

    unsigned short bul = ARRAY_AS_WORD(ibuf, 0);

    if (unpack_block(ibuf + 2, blen - 2, obuf, outputsize - olen) != bul)
      return 0;

    obuf += bul;
    olen += bul;

    ibuf += blen;
    inputsize -= blen;
  }

  return olen;
}
<commit_msg>dmo.cpp: Avoid buffer overflows during load() and terminate strings<commit_after>/*
  Adplug - Replayer for many OPL2/OPL3 audio file formats.
  Copyright (C) 1999 - 2004, 2006 Simon Peter, <dn.tlp@gmx.net>, et al.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

  dmo.cpp - TwinTeam loader by Riven the Mage <riven@ok.ru>
*/
/*
  NOTES:
  Panning is ignored.

  A WORD ist 16 bits, a DWORD is 32 bits and a BYTE is 8 bits in this context.
*/

#include <string.h>
#include <binstr.h>

#include "dmo.h"
#include "debug.h"

#define ARRAY_AS_DWORD(a, i) \
((a[i + 3] << 24) + (a[i + 2] << 16) + (a[i + 1] << 8) + a[i])
#define ARRAY_AS_WORD(a, i)	((a[i + 1] << 8) + a[i])

/* -------- Public Methods -------------------------------- */

CPlayer *CdmoLoader::factory(Copl *newopl)
{
  return new CdmoLoader(newopl);
}

bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp)
{
  int i, j;

  binistream *f = fp.open(filename);
  if (!f) return false;

  // check header
  dmo_unpacker unpacker;
  unsigned char chkhdr[16];

  f->readString((char *)chkhdr, 16);

  if (!unpacker.decrypt(chkhdr, 16))
    {
      fp.close(f);
      return false;
    }

  // get file size
  long packed_length = fp.filesize(f);
  f->seek(0);

  unsigned char *packed_module = new unsigned char [packed_length];

  // load file
  f->readString((char *)packed_module, packed_length);
  fp.close(f);

  // decrypt
  unpacker.decrypt(packed_module, packed_length);

  long unpacked_length = 0x2000 * ARRAY_AS_WORD(packed_module, 12);
  unsigned char *module = new unsigned char [unpacked_length];

  // unpack
  if (!unpacker.unpack(packed_module, packed_length, module, unpacked_length))
    {
      delete [] packed_module;
      delete [] module;
      return false;
    }

  delete [] packed_module;

  // "TwinTeam" - signed ?
  if (memcmp(module,"TwinTeam Module File""\x0D\x0A",22))
    {
      delete [] module;
      return false;
    }

  // load header
  binisstream	uf(module, unpacked_length);
  uf.setFlag(binio::BigEndian, false); uf.setFlag(binio::FloatIEEE);

  memset(&header,0,sizeof(s3mheader));

  uf.ignore(22);				// ignore DMO header ID string
  uf.readString(header.name, 28);
  header.name[27] = 0;				// ensure termination

  uf.ignore(2);				// _unk_1
  header.ordnum  = uf.readInt(2);
  header.insnum  = uf.readInt(2);
  header.patnum  = uf.readInt(2);
  uf.ignore(2);				// _unk_2
  header.is      = uf.readInt(2);
  header.it      = uf.readInt(2);

  if (header.ordnum >= 256 || header.insnum > 99 || header.patnum > 99) {
    delete [] module;
    return false;
  }

  memset(header.chanset,0xFF,32);

  for (i=0;i<9;i++)
    header.chanset[i] = 0x10 + i;

  uf.ignore(32);				// ignore panning settings for all 32 channels

  // load orders
  for(i = 0; i < 256; i++) orders[i] = uf.readInt(1);

  orders[header.ordnum] = 0xFF;

  // load pattern lengths
  unsigned short my_patlen[100];
  for(i = 0; i < 100; i++) my_patlen[i] = uf.readInt(2);

  // load instruments
  for (i = 0; i < header.insnum; i++)
    {
      memset(&inst[i],0,sizeof(s3minst));

      uf.readString(inst[i].name, 28);
      inst[i].name[27] = 0;

      inst[i].volume = uf.readInt(1);
      inst[i].dsk    = uf.readInt(1);
      inst[i].c2spd  = uf.readInt(4);
      inst[i].type   = uf.readInt(1);
      inst[i].d00    = uf.readInt(1);
      inst[i].d01    = uf.readInt(1);
      inst[i].d02    = uf.readInt(1);
      inst[i].d03    = uf.readInt(1);
      inst[i].d04    = uf.readInt(1);
      inst[i].d05    = uf.readInt(1);
      inst[i].d06    = uf.readInt(1);
      inst[i].d07    = uf.readInt(1);
      inst[i].d08    = uf.readInt(1);
      inst[i].d09    = uf.readInt(1);
      inst[i].d0a    = uf.readInt(1);
      /*
       * Originally, riven sets d0b = d0a and ignores 1 byte in the
       * stream, but i guess this was a typo, so i read it here.
       */
      inst[i].d0b    = uf.readInt(1);
    }

  // load patterns
  for (i = 0; i < header.patnum; i++) {
    long cur_pos = uf.pos();

    for (j = 0; j < 64; j++) {
      while (1) {
	unsigned char token = uf.readInt(1);

	if (!token)
	  break;

	unsigned char chan = token & 31;

	// note + instrument ?
	if (token & 32) {
	  unsigned char bufbyte = uf.readInt(1);

	  pattern[i][j][chan].note = bufbyte & 15;
	  pattern[i][j][chan].oct = bufbyte >> 4;
	  pattern[i][j][chan].instrument = uf.readInt(1);
	}

	// volume ?
	if (token & 64)
	  pattern[i][j][chan].volume = uf.readInt(1);

	// command ?
	if (token & 128) {
	  pattern[i][j][chan].command = uf.readInt(1);
	  pattern[i][j][chan].info = uf.readInt(1);
	}
      }
    }

    // TODO: sanitiy checking my_patlen[i] here might be a good idea
    uf.seek(cur_pos + my_patlen[i]);
  }

  delete [] module;
  rewind(0);
  return true;
}

std::string CdmoLoader::gettype()
{
  return std::string("TwinTeam (packed S3M)");
}

std::string CdmoLoader::getauthor()
{
  /*
    All available .DMO modules written by one composer. And because all .DMO
    stuff was lost due to hd crash (TwinTeam guys said this), there are
    never(?) be another.
  */
  return std::string("Benjamin GERARDIN");
}

/* -------- Private Methods ------------------------------- */

unsigned short CdmoLoader::dmo_unpacker::brand(unsigned short range)
{
  bseed *= 0x08088405U;
  bseed++;

  return (uint64_t)bseed * range >> 32;
}

bool CdmoLoader::dmo_unpacker::decrypt(unsigned char *buf, size_t len)
{
  if (len < headersize)
    return false;

  bseed = ARRAY_AS_DWORD(buf, 0);

  uint32_t seed = 0;
  for (int i = 0; i <= ARRAY_AS_WORD(buf, 4); i++)
    seed += brand(0xffff);

  bseed = seed ^ ARRAY_AS_DWORD(buf, 6);

  if (ARRAY_AS_WORD(buf, 10) != brand(0xffff))
    return false;

  for (size_t i = headersize; i < len; i++)
    buf[i] ^= brand(0x100);

  buf[len - 2] = buf[len - 1] = 0;

  return true;
}

long CdmoLoader::dmo_unpacker::unpack_block(unsigned char *ibuf, size_t ilen,
					    unsigned char *obuf, size_t olen)
{
  size_t ipos = 0, opos = 0;

  // LZ77 child
  while (ipos < ilen) {
    size_t cpy = 0, ofs = 0, lit = 0;
    unsigned char code, par1, par2;

    code = ibuf[ipos++];
    par1 = ipos < ilen ? ibuf[ipos] : 0;
    par2 = ipos + 1 < ilen ? ibuf[ipos + 1] : 0;
    switch (code >> 6) {
    case 0:
      // 00xxxxxx: use (X + 1) literal bytes
      lit = (code & 0x3F) + 1;
      break;

    case 1:
      // 01xxxxxx xxxyyyyy: copy (Y + 3) bytes from offset (X + 1)
      ipos++; // for par1
      ofs = ((code & 0x3F) << 3) + ((par1 & 0xE0) >> 5) + 1;
      cpy = (par1 & 0x1F) + 3;
      break;

    case 2:
      // 10xxxxxx xyyyzzzz: copy (Y + 3) bytes from offset(X + 1);
      // use Z literal bytes
      ipos++; // for par1
      ofs = ((code & 0x3F) << 1) + (par1 >> 7) + 1;
      cpy = ((par1 & 0x70) >> 4) + 3;
      lit = par1 & 0x0F;
      break;

    case 3:
      // 11xxxxxx xxxxxxxy yyyyzzzz: copy (Y + 4) from offset X;
      // use Z literal bytes
      ipos += 2; // for par1 and par2
      ofs = ((code & 0x3F) << 7) + (par1 >> 1);
      cpy = ((par1 & 0x01) << 4) + (par2 >> 4) + 4;
      lit = par2 & 0x0F;
      break;
    }

    // check lengths and offset
    if (ipos + lit > ilen || opos + cpy + lit > olen || ofs > opos)
      return -1;

    // copy - can't use memcpy() because source and destination may overlap
    for (size_t i = 0; i < cpy; i++)
      obuf[opos + i] = obuf[opos - ofs + i];
    opos += cpy;

    // copy literal bytes
    while (lit--)
      obuf[opos++] = ibuf[ipos++];
  }

  return opos;
}

size_t CdmoLoader::dmo_unpacker::unpack(unsigned char *ibuf, size_t inputsize,
					unsigned char *obuf, size_t outputsize)
{
  if (inputsize < headersize + 2)
    return 0;

  unsigned short block_count = ARRAY_AS_WORD(ibuf, headersize);
  unsigned block_start = headersize + 2 + 2 * block_count;

  if (inputsize < block_start)
    return 0;

  unsigned char *block_length = &ibuf[headersize + 2];
  ibuf += block_start;
  inputsize -= block_start;

  size_t olen = 0;
  for (int i = 0; i < block_count; i++) {
    unsigned short blen = ARRAY_AS_WORD(block_length, 2 * i);
    if (blen < 2 || blen > inputsize)
      return 0;

    unsigned short bul = ARRAY_AS_WORD(ibuf, 0);

    if (unpack_block(ibuf + 2, blen - 2, obuf, outputsize - olen) != bul)
      return 0;

    obuf += bul;
    olen += bul;

    ibuf += blen;
    inputsize -= blen;
  }

  return olen;
}
<|endoftext|>


<commit_before>/*!
 * \author ddubois
 * \date 12-Sep-18.
 */

#include "shaderpack_validator.hpp"
#include "../json_utils.hpp"
#include "../../util/utils.hpp"
#include "../../../tests/src/general_test_setup.hpp"

#ifdef ERROR
#undef ERROR
#endif

namespace nova {
    /*!
     * \brief All the default values for a JSON pipeline
     *
     * If a field is in `pipeline_data` but not in this structure, it is a required field and cannot be given a
     * default value. It will thus cause an exception
     */
     nlohmann::json default_graphics_pipeline =
        {
            {"parentName", ""},
            {"defines", std::array<std::string, 0>{}},
            {"states", std::array<std::string, 0>{}},
            {"frontFace", nlohmann::json::object_t()},
            {"backFace", nlohmann::json::object_t()},
            {"fallback", ""},
            {"depthBias", 0},
            {"slopeScaledDepthBias", 0},
            {"stencilRef", 0},
            {"stencilReadMask", 0},
            {"stencilWriteMask", 0},
            {"msaaSupport", "None"},
            {"primitiveMode", "Triangles"},
            {"sourceBlendFactor", "One"},
            {"destinationBlendFactor", "Zero"},
            {"alphaSrc", "One"},
            {"alphaDst", "Zero"},
            {"depthFunc", "Less"},
            {"renderQueue", "Opaque"},
            {"fragmentShader", ""},
            {"tessellationControlShader", ""},
            {"tessellationEvaluationShader", ""},
            {"geometryShader", ""}
        };

    std::vector<std::string> required_graphics_pipeline_fields = {"name", "pass", "vertexFields", "vertexShader"};

    nlohmann::json default_texture_format = {{"pixelFormat", "RGBA8"}, {"dimensionType", "Absolute"}};

    void ensure_field_exists(nlohmann::json &j, const std::string &field_name, const std::string &context, const nlohmann::json &default_value, validation_report &report);

#define PIPELINE_MSG(name, msg) "Pipeline " + name + ": " + msg

    validation_report validate_graphics_pipeline(nlohmann::json &pipeline_json) {
        validation_report report;
        const std::string name = get_json_value<std::string>(pipeline_json, "name").value_or("<NAME_MISSING>");
        // Don't need to check for the name's existence here, it'll be checked with the rest of the required fields

        const std::string pipeline_context = "Pipeline " + name;
        // Check non-required fields first
        for(const auto &str : default_graphics_pipeline.items()) {
            ensure_field_exists(pipeline_json, str.key(), pipeline_context, default_graphics_pipeline, report);
        }

        // Check required items
        report.errors.reserve(required_graphics_pipeline_fields.size());
        for(const std::string &field_name : required_graphics_pipeline_fields) {
            const auto &itr = pipeline_json.find(field_name);
            if(itr == pipeline_json.end()) {
                report.errors.emplace_back(PIPELINE_MSG(name, "Missing field " + field_name));
            }
        }

        return report;
    }

#define RESOURCES_MSG(msg) (std::string("Resources file: ") + msg)

    validation_report validate_shaderpack_resources_data(nlohmann::json &resources_json) {
        validation_report report;
        bool missing_textures = false;

        const auto &textures_itr = resources_json.find("textures");
        if(textures_itr == resources_json.end()) {
            missing_textures = true;

        } else {
            auto &textures_array = *textures_itr;
            if(textures_array.empty()) {
                missing_textures = true;

            } else {
                for(auto &tex : textures_array) {
                    const validation_report texture_report = validate_texture_data(tex);
                    report.merge_in(texture_report);
                }
            }
        }

        if(missing_textures) {
            report.warnings.emplace_back(RESOURCES_MSG("Missing dynamic resources. If you ONLY use the backbuffer in your shaderpack, you can ignore this message"));
        }

        const nlohmann::json::iterator &samplers_itr = resources_json.find("samplers");
        if(samplers_itr == resources_json.end()) {
            if(!missing_textures) {
                report.errors.emplace_back(RESOURCES_MSG("No samplers defined, but dynamic textures are defined. You need to define your own samplers to access a texture with"));
            }

        } else {
            nlohmann::json &all_samplers = *samplers_itr;
            if(!all_samplers.is_array()) {
                report.errors.emplace_back(RESOURCES_MSG("Samplers array must be an array, but like it isn't"));

            } else {
                for(nlohmann::json &sampler : all_samplers) {
                    const validation_report sampler_report = validate_sampler_data(sampler);
                    report.merge_in(sampler_report);
                }
            }
        }

        return report;
    }

#define TEXTURE_MSG(name, msg) "Texture " + name + ": " + msg

    validation_report validate_texture_data(nlohmann::json &texture_json) {
        validation_report report;
        const auto name_maybe = get_json_value<std::string>(texture_json, "name");
        std::string name;
        if(name_maybe) {
            name = name_maybe.value();

        } else {
            name = "<NAME_MISSING>";
            texture_json["name"] = name;
            report.errors.emplace_back(TEXTURE_MSG(name, "Missing field name"));
        }

        const auto format_itr = texture_json.find("format");
        if(format_itr == texture_json.end()) {
            report.errors.emplace_back(TEXTURE_MSG(name, "Missing field format"));

        } else {
            const validation_report format_report = validate_texture_format(*format_itr, name);
            report.merge_in(format_report);
        }

        return report;
    }

#define FORMAT_MSG(tex_name, msg) "Format of texture " + tex_name + ": " + msg

    validation_report validate_texture_format(nlohmann::json &format_json, const std::string &texture_name) {
        validation_report report;

        ensure_field_exists(format_json, "pixelFormat", "Format of texture " + texture_name, default_texture_format, report);
        ensure_field_exists(format_json, "dimensionType", "Format of texture " + texture_name, default_texture_format, report);

        const bool missing_width = format_json.find("width") == format_json.end();
        if(missing_width) {
            report.errors.emplace_back(FORMAT_MSG(texture_name, "Missing field width"));
        }

        const bool missing_height = format_json.find("height") == format_json.end();
        if(missing_height) {
            report.errors.emplace_back(FORMAT_MSG(texture_name, "Missing field height"));
        }

        return report;
    }

#define SAMPLER_MSG(name, msg) "Sampler " + name + ": " + msg

    validation_report validate_sampler_data(nlohmann::json &sampler_json) {
        validation_report report;
        const std::string name = get_json_value<std::string>(sampler_json, "name").value_or("<NAME_MISSING>");
        if(name == "<NAME_MISSING>") {
            report.errors.emplace_back(SAMPLER_MSG(name, "Missing field name"));
        }

        const bool missing_filter = sampler_json.find("filter") == sampler_json.end();
        if(missing_filter) {
            report.errors.emplace_back(SAMPLER_MSG(name, "Missing field filter"));
        }

        const bool missing_wrap_mode = sampler_json.find("wrapMode") == sampler_json.end();
        if(missing_wrap_mode) {
            report.errors.emplace_back(SAMPLER_MSG(name, "Missing field wrapMode"));
        }

        return report;
    }

#define MATERIAL_MSG(name, error) "Material " + name + ": " + error
#define MATERIAL_PASS_MSG(mat_name, pass_name, error) "Material pass " + pass_name + " in material " + mat_name + ": " + error

    validation_report validate_material(nlohmann::json &material_json) {
        validation_report report;

        const std::string name = get_json_value<std::string>(material_json, "name").value_or("<NAME_MISSING>");
        if(name == "<NAME_MISSING>") {
            report.errors.emplace_back(MATERIAL_MSG(name, "Missing material name"));
        }

        const bool missing_geometry_filter = material_json.find("filter") == material_json.end();
        if(missing_geometry_filter) {
            report.errors.emplace_back(MATERIAL_MSG(name, "Missing geometry filter"));
        }

        const bool missing_passes = material_json.find("passes") == material_json.end();
        if(missing_passes) {
            report.errors.emplace_back(MATERIAL_MSG(name, "Missing material passes"));

        } else {
            const nlohmann::json &passes_json = material_json.at("passes");
            if(!passes_json.is_array()) {
                report.errors.emplace_back(MATERIAL_MSG(name, "Passes field must be an array"));
                return report;

            } else if(passes_json.empty()) {
                report.errors.emplace_back(MATERIAL_MSG(name, "Passes field must have at least one item"));
                return report;
            }

            for(const auto &pass_json : passes_json) {
                const std::string pass_name = get_json_value<std::string>(pass_json, "name").value_or("<NAME_MISSING>");
                if(pass_name == "<NAME_MISSING>") {
                    report.errors.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Missing field name"));
                }

                const auto pipeline_maybe = get_json_value<std::string>(pass_json, "pipeline");
                if(!pipeline_maybe) {
                    report.errors.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Missing field pipeline"));
                }

                const auto bindings_itr = pass_json.find("bindings");
                if(bindings_itr == pass_json.end()) {
                    report.warnings.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Missing field bindings"));
                } else {
                    const nlohmann::json &bindings = *bindings_itr;
                    if(bindings.empty()) {
                        report.warnings.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Field bindings exists but it's empty"));
                    }
                }
            }
        }

        return report;
    }

    void ensure_field_exists(nlohmann::json &j, const std::string &field_name, const std::string &context, const nlohmann::json &default_value, validation_report &report) {
        if(j.find(field_name) == j.end()) {
            j[field_name] = default_value[field_name];
            report.warnings.emplace_back(context + ": Missing field " + field_name + ". A default value of '" + j[field_name].dump() + "' will be used");
        }
    }

    void print(const validation_report &report) {
        for(const auto &error : report.errors) {
            NOVA_LOG(ERROR) << error;
        }

        for(const auto &warning : report.warnings) {
            NOVA_LOG(DEBUG) << warning;
        }
    }

    void validation_report::merge_in(const validation_report &other) {
        errors.insert(errors.end(), other.errors.begin(), other.errors.end());
        warnings.insert(warnings.begin(), other.warnings.begin(), other.warnings.end());
    }
}  // namespace nova
<commit_msg>clang-tidy: bugprone-macro-parentheses<commit_after>/*!
 * \author ddubois
 * \date 12-Sep-18.
 */

#include "shaderpack_validator.hpp"
#include "../json_utils.hpp"
#include "../../util/utils.hpp"
#include "../../../tests/src/general_test_setup.hpp"

#ifdef ERROR
#undef ERROR
#endif

namespace nova {
    /*!
     * \brief All the default values for a JSON pipeline
     *
     * If a field is in `pipeline_data` but not in this structure, it is a required field and cannot be given a
     * default value. It will thus cause an exception
     */
     nlohmann::json default_graphics_pipeline =
        {
            {"parentName", ""},
            {"defines", std::array<std::string, 0>{}},
            {"states", std::array<std::string, 0>{}},
            {"frontFace", nlohmann::json::object_t()},
            {"backFace", nlohmann::json::object_t()},
            {"fallback", ""},
            {"depthBias", 0},
            {"slopeScaledDepthBias", 0},
            {"stencilRef", 0},
            {"stencilReadMask", 0},
            {"stencilWriteMask", 0},
            {"msaaSupport", "None"},
            {"primitiveMode", "Triangles"},
            {"sourceBlendFactor", "One"},
            {"destinationBlendFactor", "Zero"},
            {"alphaSrc", "One"},
            {"alphaDst", "Zero"},
            {"depthFunc", "Less"},
            {"renderQueue", "Opaque"},
            {"fragmentShader", ""},
            {"tessellationControlShader", ""},
            {"tessellationEvaluationShader", ""},
            {"geometryShader", ""}
        };

    std::vector<std::string> required_graphics_pipeline_fields = {"name", "pass", "vertexFields", "vertexShader"};

    nlohmann::json default_texture_format = {{"pixelFormat", "RGBA8"}, {"dimensionType", "Absolute"}};

    void ensure_field_exists(nlohmann::json &j, const std::string &field_name, const std::string &context, const nlohmann::json &default_value, validation_report &report);

#define PIPELINE_MSG(name, msg) ("Pipeline " + (name) + ": " + (msg))

    validation_report validate_graphics_pipeline(nlohmann::json &pipeline_json) {
        validation_report report;
        const std::string name = get_json_value<std::string>(pipeline_json, "name").value_or("<NAME_MISSING>");
        // Don't need to check for the name's existence here, it'll be checked with the rest of the required fields

        const std::string pipeline_context = "Pipeline " + name;
        // Check non-required fields first
        for(const auto &str : default_graphics_pipeline.items()) {
            ensure_field_exists(pipeline_json, str.key(), pipeline_context, default_graphics_pipeline, report);
        }

        // Check required items
        report.errors.reserve(required_graphics_pipeline_fields.size());
        for(const std::string &field_name : required_graphics_pipeline_fields) {
            const auto &itr = pipeline_json.find(field_name);
            if(itr == pipeline_json.end()) {
                report.errors.emplace_back(PIPELINE_MSG(name, "Missing field " + field_name));
            }
        }

        return report;
    }

#define RESOURCES_MSG(msg) (std::string("Resources file: ") + (msg))

    validation_report validate_shaderpack_resources_data(nlohmann::json &resources_json) {
        validation_report report;
        bool missing_textures = false;

        const auto &textures_itr = resources_json.find("textures");
        if(textures_itr == resources_json.end()) {
            missing_textures = true;

        } else {
            auto &textures_array = *textures_itr;
            if(textures_array.empty()) {
                missing_textures = true;

            } else {
                for(auto &tex : textures_array) {
                    const validation_report texture_report = validate_texture_data(tex);
                    report.merge_in(texture_report);
                }
            }
        }

        if(missing_textures) {
            report.warnings.emplace_back(RESOURCES_MSG("Missing dynamic resources. If you ONLY use the backbuffer in your shaderpack, you can ignore this message"));
        }

        const nlohmann::json::iterator &samplers_itr = resources_json.find("samplers");
        if(samplers_itr == resources_json.end()) {
            if(!missing_textures) {
                report.errors.emplace_back(RESOURCES_MSG("No samplers defined, but dynamic textures are defined. You need to define your own samplers to access a texture with"));
            }

        } else {
            nlohmann::json &all_samplers = *samplers_itr;
            if(!all_samplers.is_array()) {
                report.errors.emplace_back(RESOURCES_MSG("Samplers array must be an array, but like it isn't"));

            } else {
                for(nlohmann::json &sampler : all_samplers) {
                    const validation_report sampler_report = validate_sampler_data(sampler);
                    report.merge_in(sampler_report);
                }
            }
        }

        return report;
    }

#define TEXTURE_MSG(name, msg) ("Texture " + (name) + ": " + (msg))

    validation_report validate_texture_data(nlohmann::json &texture_json) {
        validation_report report;
        const auto name_maybe = get_json_value<std::string>(texture_json, "name");
        std::string name;
        if(name_maybe) {
            name = name_maybe.value();

        } else {
            name = "<NAME_MISSING>";
            texture_json["name"] = name;
            report.errors.emplace_back(TEXTURE_MSG(name, "Missing field name"));
        }

        const auto format_itr = texture_json.find("format");
        if(format_itr == texture_json.end()) {
            report.errors.emplace_back(TEXTURE_MSG(name, "Missing field format"));

        } else {
            const validation_report format_report = validate_texture_format(*format_itr, name);
            report.merge_in(format_report);
        }

        return report;
    }

#define FORMAT_MSG(tex_name, msg) ("Format of texture " + (tex_name) + ": " + (msg))

    validation_report validate_texture_format(nlohmann::json &format_json, const std::string &texture_name) {
        validation_report report;

        ensure_field_exists(format_json, "pixelFormat", "Format of texture " + texture_name, default_texture_format, report);
        ensure_field_exists(format_json, "dimensionType", "Format of texture " + texture_name, default_texture_format, report);

        const bool missing_width = format_json.find("width") == format_json.end();
        if(missing_width) {
            report.errors.emplace_back(FORMAT_MSG(texture_name, "Missing field width"));
        }

        const bool missing_height = format_json.find("height") == format_json.end();
        if(missing_height) {
            report.errors.emplace_back(FORMAT_MSG(texture_name, "Missing field height"));
        }

        return report;
    }

#define SAMPLER_MSG(name, msg) ("Sampler " + (name) + ": " + (msg))

    validation_report validate_sampler_data(nlohmann::json &sampler_json) {
        validation_report report;
        const std::string name = get_json_value<std::string>(sampler_json, "name").value_or("<NAME_MISSING>");
        if(name == "<NAME_MISSING>") {
            report.errors.emplace_back(SAMPLER_MSG(name, "Missing field name"));
        }

        const bool missing_filter = sampler_json.find("filter") == sampler_json.end();
        if(missing_filter) {
            report.errors.emplace_back(SAMPLER_MSG(name, "Missing field filter"));
        }

        const bool missing_wrap_mode = sampler_json.find("wrapMode") == sampler_json.end();
        if(missing_wrap_mode) {
            report.errors.emplace_back(SAMPLER_MSG(name, "Missing field wrapMode"));
        }

        return report;
    }

#define MATERIAL_MSG(name, error) ("Material " + (name) + ": " + (error))
#define MATERIAL_PASS_MSG(mat_name, pass_name, error) ("Material pass " + (pass_name) + " in material " + (mat_name) + ": " + (error))

    validation_report validate_material(nlohmann::json &material_json) {
        validation_report report;

        const std::string name = get_json_value<std::string>(material_json, "name").value_or("<NAME_MISSING>");
        if(name == "<NAME_MISSING>") {
            report.errors.emplace_back(MATERIAL_MSG(name, "Missing material name"));
        }

        const bool missing_geometry_filter = material_json.find("filter") == material_json.end();
        if(missing_geometry_filter) {
            report.errors.emplace_back(MATERIAL_MSG(name, "Missing geometry filter"));
        }

        const bool missing_passes = material_json.find("passes") == material_json.end();
        if(missing_passes) {
            report.errors.emplace_back(MATERIAL_MSG(name, "Missing material passes"));

        } else {
            const nlohmann::json &passes_json = material_json.at("passes");
            if(!passes_json.is_array()) {
                report.errors.emplace_back(MATERIAL_MSG(name, "Passes field must be an array"));
                return report;

            } else if(passes_json.empty()) {
                report.errors.emplace_back(MATERIAL_MSG(name, "Passes field must have at least one item"));
                return report;
            }

            for(const auto &pass_json : passes_json) {
                const std::string pass_name = get_json_value<std::string>(pass_json, "name").value_or("<NAME_MISSING>");
                if(pass_name == "<NAME_MISSING>") {
                    report.errors.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Missing field name"));
                }

                const auto pipeline_maybe = get_json_value<std::string>(pass_json, "pipeline");
                if(!pipeline_maybe) {
                    report.errors.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Missing field pipeline"));
                }

                const auto bindings_itr = pass_json.find("bindings");
                if(bindings_itr == pass_json.end()) {
                    report.warnings.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Missing field bindings"));
                } else {
                    const nlohmann::json &bindings = *bindings_itr;
                    if(bindings.empty()) {
                        report.warnings.emplace_back(MATERIAL_PASS_MSG(name, pass_name, "Field bindings exists but it's empty"));
                    }
                }
            }
        }

        return report;
    }

    void ensure_field_exists(nlohmann::json &j, const std::string &field_name, const std::string &context, const nlohmann::json &default_value, validation_report &report) {
        if(j.find(field_name) == j.end()) {
            j[field_name] = default_value[field_name];
            report.warnings.emplace_back(context + ": Missing field " + field_name + ". A default value of '" + j[field_name].dump() + "' will be used");
        }
    }

    void print(const validation_report &report) {
        for(const auto &error : report.errors) {
            NOVA_LOG(ERROR) << error;
        }

        for(const auto &warning : report.warnings) {
            NOVA_LOG(DEBUG) << warning;
        }
    }

    void validation_report::merge_in(const validation_report &other) {
        errors.insert(errors.end(), other.errors.begin(), other.errors.end());
        warnings.insert(warnings.begin(), other.warnings.begin(), other.warnings.end());
    }
}  // namespace nova
<|endoftext|>


<commit_before>/* This file is part of Ingen.
 * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
 *
 * Ingen is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <cassert>
#include "raul/Atom.hpp"
#include "interface/EngineInterface.hpp"
#include "client/NodeModel.hpp"
#include "client/PatchModel.hpp"
#include "client/PluginUI.hpp"
#include "App.hpp"
#include "GladeFactory.hpp"
#include "NodeControlWindow.hpp"
#include "NodeModule.hpp"
#include "PatchCanvas.hpp"
#include "PatchWindow.hpp"
#include "Port.hpp"
#include "RenameWindow.hpp"
#include "SubpatchModule.hpp"
#include "WindowFactory.hpp"
#include "Configuration.hpp"
#include "NodeMenu.hpp"

using namespace std;

namespace Ingen {
namespace GUI {

NodeModule::NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node)
	: FlowCanvas::Module(canvas, node->path().name())
	, _node(node)
	, _gui_widget(NULL)
	, _gui_window(NULL)
{
	assert(_node);

	node->signal_new_port.connect(sigc::bind(sigc::mem_fun(this, &NodeModule::add_port), true));
	node->signal_removed_port.connect(sigc::hide_return(sigc::mem_fun(this, &NodeModule::remove_port)));
	node->signal_variable.connect(sigc::mem_fun(this, &NodeModule::set_variable));
	node->signal_property.connect(sigc::mem_fun(this, &NodeModule::set_property));
	node->signal_renamed.connect(sigc::mem_fun(this, &NodeModule::rename));
}

NodeModule::~NodeModule()
{
	NodeControlWindow* win = App::instance().window_factory()->control_window(_node);

	if (win) {
		// Should remove from window factory via signal
		delete win;
	}
}

void
NodeModule::create_menu()
{
	Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference();
	xml->get_widget_derived("object_menu", _menu);
	_menu->init(_node);
	_menu->signal_embed_gui.connect(sigc::mem_fun(this, &NodeModule::embed_gui));
	_menu->signal_popup_gui.connect(sigc::hide_return(sigc::mem_fun(this, &NodeModule::popup_gui)));

	set_menu(_menu);
}

boost::shared_ptr<NodeModule>
NodeModule::create(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node, bool human)
{
	boost::shared_ptr<NodeModule> ret;

	SharedPtr<PatchModel> patch = PtrCast<PatchModel>(node);
	if (patch)
		ret = boost::shared_ptr<NodeModule>(new SubpatchModule(canvas, patch));
	else
		ret = boost::shared_ptr<NodeModule>(new NodeModule(canvas, node));

	for (GraphObject::Variables::const_iterator m = node->variables().begin(); m != node->variables().end(); ++m)
		ret->set_variable(m->first, m->second);

	for (NodeModel::Ports::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p) {
		ret->add_port(*p, false);
	}

	if (human)
		ret->show_human_names(human); // FIXME: double port iteration

	ret->resize();
	ret->set_stacked_border(node->polyphonic());

	return ret;
}

void
NodeModule::show_human_names(bool b)
{
	if (b && node()->plugin()) {
		Glib::Mutex::Lock lock(App::instance().world()->rdf_world->mutex());
		set_name(((PluginModel*)node()->plugin())->human_name());
	} else {
		b = false;
	}

	if (!b)
		set_name(node()->symbol());

	uint32_t index = 0;
	for (PortVector::const_iterator i = ports().begin(); i != ports().end(); ++i) {
		string name = node()->port(index)->symbol();
		if (b) {
			string hn = ((PluginModel*)node()->plugin())->port_human_name(index);
			if (hn != "")
				name = hn;
		}
		(*i)->set_name(name);
		++index;
	}

	resize();
}

void
NodeModule::value_changed(uint32_t index, const Atom& value)
{
	float control = 0.0f;
	switch (value.type()) {
	case Atom::FLOAT:
		control = value.get_float();
		if (_plugin_ui) {
			SLV2UIInstance inst = _plugin_ui->instance();
			const LV2UI_Descriptor* ui_descriptor = slv2_ui_instance_get_descriptor(inst);
			LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(inst);
			if (ui_descriptor->port_event)
				ui_descriptor->port_event(ui_handle, index, 4, 0, &control);
		}
		break;
	case Atom::STRING:
		cout << "Port value type is a string? (\"" << value.get_string() << "\")" << endl;
		break;
	default:
		break;
	}
}

void
NodeModule::embed_gui(bool embed)
{
	if (embed) {

		if (_gui_window) {
			cerr << "LV2 GUI already popped up, cannot embed" << endl;
			return;
		}

		if (!_plugin_ui) {
			const PluginModel* const pm = dynamic_cast<const PluginModel*>(_node->plugin());
			assert(pm);
			_plugin_ui = pm->ui(App::instance().world(), _node);
		}

		if (_plugin_ui) {
			GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance());
			_gui_widget = Glib::wrap(c_widget);
			assert(_gui_widget);

			Gtk::Container* container = new Gtk::EventBox();
			container->set_name("ingen_embedded_node_gui_container");
			container->add(*_gui_widget);
			FlowCanvas::Module::embed(container);
		} else {
			cerr << "ERROR: Failed to create LV2 UI" << endl;
		}

		if (_gui_widget) {
			_gui_widget->show_all();

			for (NodeModel::Ports::const_iterator p = _node->ports().begin();
					p != _node->ports().end(); ++p)
				if ((*p)->type().is_control() && (*p)->is_output())
					App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", true);
		}

	} else { // un-embed

		FlowCanvas::Module::embed(NULL);
		_plugin_ui.reset();

		for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p)
			if ((*p)->type().is_control() && (*p)->is_output())
				App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", false);
	}

	if (embed && _embed_item) {
		initialise_gui_values();
		set_base_color(0x212222FF);
	} else {
		set_default_base_color();
	}

	resize();
}

void
NodeModule::rename()
{
	set_name(_node->path().name());
	resize();
}

void
NodeModule::add_port(SharedPtr<PortModel> port, bool resize_to_fit)
{
	uint32_t index = _ports.size(); // FIXME: kludge, engine needs to tell us this

	string name = port->path().name();
	if (App::instance().configuration()->name_style() == Configuration::HUMAN && node()->plugin()) {
		string hn = ((PluginModel*)node()->plugin())->port_human_name(index);
		if (hn != "")
			name = hn;
	}

	Module::add_port(boost::shared_ptr<Port>(
			new Port(PtrCast<NodeModule>(shared_from_this()), port, name)));

	port->signal_value_changed.connect(sigc::bind<0>(
			sigc::mem_fun(this, &NodeModule::value_changed), index));

	if (resize_to_fit)
		resize();
}

boost::shared_ptr<Port>
NodeModule::port(boost::shared_ptr<PortModel> model)
{
	for (PortVector::const_iterator p = ports().begin(); p != ports().end(); ++p) {
		SharedPtr<Port> port = PtrCast<Port>(*p);
		if (port->model() == model) {
			cout << "FOUND: " << model->path() << endl;
			return port;
		}
	}
	return boost::shared_ptr<Port>();
}

void
NodeModule::remove_port(SharedPtr<PortModel> model)
{
	SharedPtr<Port> p = port(model);
	if (p) {
		Module::remove_port(p);
		p.reset();
	} else {
		cerr << "WARNING: Failed to find port on module: " << model->path() << endl;
	}
}

bool
NodeModule::popup_gui()
{
#ifdef HAVE_SLV2
	if (_node->plugin() && _node->plugin()->type() == PluginModel::LV2) {
		if (_plugin_ui) {
			cerr << "LV2 GUI already embedded, cannot pop up" << endl;
			return false;
		}

		const PluginModel* const plugin = dynamic_cast<const PluginModel*>(_node->plugin());
		assert(plugin);

		_plugin_ui = plugin->ui(App::instance().world(), _node);

		if (_plugin_ui) {
			GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance());
			_gui_widget = Glib::wrap(c_widget);

			_gui_window = new Gtk::Window();
			_gui_window->add(*_gui_widget);
			_gui_widget->show_all();
			initialise_gui_values();

			_gui_window->signal_unmap().connect(
					sigc::mem_fun(this, &NodeModule::on_gui_window_close));
			_gui_window->present();

			return true;
		} else {
			cerr << "No LV2 GUI" << endl;
		}
	}
#endif
	return false;
}

void
NodeModule::on_gui_window_close()
{
	delete _gui_window;
	_gui_window = NULL;
	_plugin_ui.reset();
	_gui_widget = NULL;
}

void
NodeModule::initialise_gui_values()
{
	uint32_t index=0;
	for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) {
		if ((*p)->type().is_control())
			value_changed(index, (*p)->value());
		++index;
	}
}

void
NodeModule::show_control_window()
{
	App::instance().window_factory()->present_controls(_node);
}

void
NodeModule::on_double_click(GdkEventButton* ev)
{
	if ( ! popup_gui() )
		show_control_window();
}

void
NodeModule::store_location()
{
	const float x = static_cast<float>(property_x());
	const float y = static_cast<float>(property_y());

	const Atom& existing_x = _node->get_variable("ingenuity:canvas-x");
	const Atom& existing_y = _node->get_variable("ingenuity:canvas-y");

	if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT
			|| existing_x.get_float() != x || existing_y.get_float() != y) {
		App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-x", Atom(x));
		App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-y", Atom(y));
	}
}

void
NodeModule::set_variable(const string& key, const Atom& value)
{
	if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT)
		move_to(value.get_float(), property_y());
	else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT)
		move_to(property_x(), value.get_float());
}

void
NodeModule::set_property(const string& key, const Atom& value)
{
	if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) {
		set_stacked_border(value.get_bool());
	} else if (key == "ingen:selected" && value.type() == Atom::BOOL) {
		if (value.get_bool() != selected()) {
			if (value.get_bool())
				_canvas.lock()->select_item(shared_from_this());
			else
				_canvas.lock()->unselect_item(shared_from_this());
		}
	}
}

void
NodeModule::set_selected(bool b)
{
	if (b != selected()) {
		Module::set_selected(b);
		if (App::instance().signal())
			App::instance().engine()->set_property(_node->path(), "ingen:selected", b);
	}
}

} // namespace GUI
} // namespace Ingen
<commit_msg>Shutup.<commit_after>/* This file is part of Ingen.
 * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
 *
 * Ingen is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <cassert>
#include "raul/Atom.hpp"
#include "interface/EngineInterface.hpp"
#include "client/NodeModel.hpp"
#include "client/PatchModel.hpp"
#include "client/PluginUI.hpp"
#include "App.hpp"
#include "GladeFactory.hpp"
#include "NodeControlWindow.hpp"
#include "NodeModule.hpp"
#include "PatchCanvas.hpp"
#include "PatchWindow.hpp"
#include "Port.hpp"
#include "RenameWindow.hpp"
#include "SubpatchModule.hpp"
#include "WindowFactory.hpp"
#include "Configuration.hpp"
#include "NodeMenu.hpp"

using namespace std;

namespace Ingen {
namespace GUI {

NodeModule::NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node)
	: FlowCanvas::Module(canvas, node->path().name())
	, _node(node)
	, _gui_widget(NULL)
	, _gui_window(NULL)
{
	assert(_node);

	node->signal_new_port.connect(sigc::bind(sigc::mem_fun(this, &NodeModule::add_port), true));
	node->signal_removed_port.connect(sigc::hide_return(sigc::mem_fun(this, &NodeModule::remove_port)));
	node->signal_variable.connect(sigc::mem_fun(this, &NodeModule::set_variable));
	node->signal_property.connect(sigc::mem_fun(this, &NodeModule::set_property));
	node->signal_renamed.connect(sigc::mem_fun(this, &NodeModule::rename));
}

NodeModule::~NodeModule()
{
	NodeControlWindow* win = App::instance().window_factory()->control_window(_node);

	if (win) {
		// Should remove from window factory via signal
		delete win;
	}
}

void
NodeModule::create_menu()
{
	Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference();
	xml->get_widget_derived("object_menu", _menu);
	_menu->init(_node);
	_menu->signal_embed_gui.connect(sigc::mem_fun(this, &NodeModule::embed_gui));
	_menu->signal_popup_gui.connect(sigc::hide_return(sigc::mem_fun(this, &NodeModule::popup_gui)));

	set_menu(_menu);
}

boost::shared_ptr<NodeModule>
NodeModule::create(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node, bool human)
{
	boost::shared_ptr<NodeModule> ret;

	SharedPtr<PatchModel> patch = PtrCast<PatchModel>(node);
	if (patch)
		ret = boost::shared_ptr<NodeModule>(new SubpatchModule(canvas, patch));
	else
		ret = boost::shared_ptr<NodeModule>(new NodeModule(canvas, node));

	for (GraphObject::Variables::const_iterator m = node->variables().begin(); m != node->variables().end(); ++m)
		ret->set_variable(m->first, m->second);

	for (NodeModel::Ports::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p) {
		ret->add_port(*p, false);
	}

	if (human)
		ret->show_human_names(human); // FIXME: double port iteration

	ret->resize();
	ret->set_stacked_border(node->polyphonic());

	return ret;
}

void
NodeModule::show_human_names(bool b)
{
	if (b && node()->plugin()) {
		Glib::Mutex::Lock lock(App::instance().world()->rdf_world->mutex());
		set_name(((PluginModel*)node()->plugin())->human_name());
	} else {
		b = false;
	}

	if (!b)
		set_name(node()->symbol());

	uint32_t index = 0;
	for (PortVector::const_iterator i = ports().begin(); i != ports().end(); ++i) {
		string name = node()->port(index)->symbol();
		if (b) {
			string hn = ((PluginModel*)node()->plugin())->port_human_name(index);
			if (hn != "")
				name = hn;
		}
		(*i)->set_name(name);
		++index;
	}

	resize();
}

void
NodeModule::value_changed(uint32_t index, const Atom& value)
{
	float control = 0.0f;
	switch (value.type()) {
	case Atom::FLOAT:
		control = value.get_float();
		if (_plugin_ui) {
			SLV2UIInstance inst = _plugin_ui->instance();
			const LV2UI_Descriptor* ui_descriptor = slv2_ui_instance_get_descriptor(inst);
			LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(inst);
			if (ui_descriptor->port_event)
				ui_descriptor->port_event(ui_handle, index, 4, 0, &control);
		}
		break;
	case Atom::STRING:
		cout << "Port value type is a string? (\"" << value.get_string() << "\")" << endl;
		break;
	default:
		break;
	}
}

void
NodeModule::embed_gui(bool embed)
{
	if (embed) {

		if (_gui_window) {
			cerr << "LV2 GUI already popped up, cannot embed" << endl;
			return;
		}

		if (!_plugin_ui) {
			const PluginModel* const pm = dynamic_cast<const PluginModel*>(_node->plugin());
			assert(pm);
			_plugin_ui = pm->ui(App::instance().world(), _node);
		}

		if (_plugin_ui) {
			GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance());
			_gui_widget = Glib::wrap(c_widget);
			assert(_gui_widget);

			Gtk::Container* container = new Gtk::EventBox();
			container->set_name("ingen_embedded_node_gui_container");
			container->add(*_gui_widget);
			FlowCanvas::Module::embed(container);
		} else {
			cerr << "ERROR: Failed to create LV2 UI" << endl;
		}

		if (_gui_widget) {
			_gui_widget->show_all();

			for (NodeModel::Ports::const_iterator p = _node->ports().begin();
					p != _node->ports().end(); ++p)
				if ((*p)->type().is_control() && (*p)->is_output())
					App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", true);
		}

	} else { // un-embed

		FlowCanvas::Module::embed(NULL);
		_plugin_ui.reset();

		for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p)
			if ((*p)->type().is_control() && (*p)->is_output())
				App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", false);
	}

	if (embed && _embed_item) {
		initialise_gui_values();
		set_base_color(0x212222FF);
	} else {
		set_default_base_color();
	}

	resize();
}

void
NodeModule::rename()
{
	set_name(_node->path().name());
	resize();
}

void
NodeModule::add_port(SharedPtr<PortModel> port, bool resize_to_fit)
{
	uint32_t index = _ports.size(); // FIXME: kludge, engine needs to tell us this

	string name = port->path().name();
	if (App::instance().configuration()->name_style() == Configuration::HUMAN && node()->plugin()) {
		string hn = ((PluginModel*)node()->plugin())->port_human_name(index);
		if (hn != "")
			name = hn;
	}

	Module::add_port(boost::shared_ptr<Port>(
			new Port(PtrCast<NodeModule>(shared_from_this()), port, name)));

	port->signal_value_changed.connect(sigc::bind<0>(
			sigc::mem_fun(this, &NodeModule::value_changed), index));

	if (resize_to_fit)
		resize();
}

boost::shared_ptr<Port>
NodeModule::port(boost::shared_ptr<PortModel> model)
{
	for (PortVector::const_iterator p = ports().begin(); p != ports().end(); ++p) {
		SharedPtr<Port> port = PtrCast<Port>(*p);
		if (port->model() == model)
			return port;
	}
	return boost::shared_ptr<Port>();
}

void
NodeModule::remove_port(SharedPtr<PortModel> model)
{
	SharedPtr<Port> p = port(model);
	if (p) {
		Module::remove_port(p);
		p.reset();
	} else {
		cerr << "WARNING: Failed to find port on module: " << model->path() << endl;
	}
}

bool
NodeModule::popup_gui()
{
#ifdef HAVE_SLV2
	if (_node->plugin() && _node->plugin()->type() == PluginModel::LV2) {
		if (_plugin_ui) {
			cerr << "LV2 GUI already embedded, cannot pop up" << endl;
			return false;
		}

		const PluginModel* const plugin = dynamic_cast<const PluginModel*>(_node->plugin());
		assert(plugin);

		_plugin_ui = plugin->ui(App::instance().world(), _node);

		if (_plugin_ui) {
			GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance());
			_gui_widget = Glib::wrap(c_widget);

			_gui_window = new Gtk::Window();
			_gui_window->add(*_gui_widget);
			_gui_widget->show_all();
			initialise_gui_values();

			_gui_window->signal_unmap().connect(
					sigc::mem_fun(this, &NodeModule::on_gui_window_close));
			_gui_window->present();

			return true;
		} else {
			cerr << "No LV2 GUI" << endl;
		}
	}
#endif
	return false;
}

void
NodeModule::on_gui_window_close()
{
	delete _gui_window;
	_gui_window = NULL;
	_plugin_ui.reset();
	_gui_widget = NULL;
}

void
NodeModule::initialise_gui_values()
{
	uint32_t index=0;
	for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) {
		if ((*p)->type().is_control())
			value_changed(index, (*p)->value());
		++index;
	}
}

void
NodeModule::show_control_window()
{
	App::instance().window_factory()->present_controls(_node);
}

void
NodeModule::on_double_click(GdkEventButton* ev)
{
	if ( ! popup_gui() )
		show_control_window();
}

void
NodeModule::store_location()
{
	const float x = static_cast<float>(property_x());
	const float y = static_cast<float>(property_y());

	const Atom& existing_x = _node->get_variable("ingenuity:canvas-x");
	const Atom& existing_y = _node->get_variable("ingenuity:canvas-y");

	if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT
			|| existing_x.get_float() != x || existing_y.get_float() != y) {
		App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-x", Atom(x));
		App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-y", Atom(y));
	}
}

void
NodeModule::set_variable(const string& key, const Atom& value)
{
	if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT)
		move_to(value.get_float(), property_y());
	else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT)
		move_to(property_x(), value.get_float());
}

void
NodeModule::set_property(const string& key, const Atom& value)
{
	if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) {
		set_stacked_border(value.get_bool());
	} else if (key == "ingen:selected" && value.type() == Atom::BOOL) {
		if (value.get_bool() != selected()) {
			if (value.get_bool())
				_canvas.lock()->select_item(shared_from_this());
			else
				_canvas.lock()->unselect_item(shared_from_this());
		}
	}
}

void
NodeModule::set_selected(bool b)
{
	if (b != selected()) {
		Module::set_selected(b);
		if (App::instance().signal())
			App::instance().engine()->set_property(_node->path(), "ingen:selected", b);
	}
}

} // namespace GUI
} // namespace Ingen
<|endoftext|>


<commit_before>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <memory.hpp>
#include <dispatch.hpp>
#include <map>
#include <iostream>
#include <iomanip>
#include <string>
#include <types.hpp>

namespace opencl
{
    static size_t memory_resolution = 1024; //1KB

    void setMemStepSize(size_t step_bytes)
    {
        memory_resolution = step_bytes;
    }

    size_t getMemStepSize(void)
    {
        return memory_resolution;
    }

    // Manager Class
    // Dummy used to call garbage collection at the end of the program
    class Manager
    {
        public:
        static bool initialized;
        Manager()
        {
            initialized = true;
        }

        ~Manager()
        {
            for(int i = 0; i < (int)getDeviceCount(); i++) {
                setDevice(i);
                garbageCollect();
                pinnedGarbageCollect();
            }
        }
    };

    bool Manager::initialized = false;

    static void managerInit()
    {
        if(Manager::initialized == false)
            static Manager pm = Manager();
    }

    typedef struct
    {
        bool is_free;
        bool is_unlinked;
        size_t bytes;
    } mem_info;

    static size_t used_bytes[DeviceManager::MAX_DEVICES] = {0};
    static size_t used_buffers[DeviceManager::MAX_DEVICES] = {0};
    static size_t total_bytes[DeviceManager::MAX_DEVICES] = {0};

    typedef std::map<cl::Buffer *, mem_info> mem_t;
    typedef mem_t::iterator mem_iter;
    mem_t memory_maps[DeviceManager::MAX_DEVICES];

    static void destroy(cl::Buffer *ptr)
    {
        delete ptr;
    }

    void garbageCollect()
    {
        int n = getActiveDeviceId();
        for(mem_iter iter = memory_maps[n].begin();
            iter != memory_maps[n].end(); ++iter) {

            if ((iter->second).is_free) {

                if (!(iter->second).is_unlinked) {
                    destroy(iter->first);
                    total_bytes[n] -= iter->second.bytes;
                }
            }
        }

        mem_iter memory_curr = memory_maps[n].begin();
        mem_iter memory_end  = memory_maps[n].end();

        while(memory_curr != memory_end) {
            if (memory_curr->second.is_free  && !memory_curr->second.is_unlinked) {
                memory_curr = memory_maps[n].erase(memory_curr);
            } else {
                ++memory_curr;
            }
        }
    }

    void printMemInfo(const char *msg, const int device)
    {
        std::cout << msg << std::endl;
        std::cout << "Memory Map for Device: " << device << std::endl;

        static const std::string head("|     POINTER      |    SIZE    |  AF LOCK  | USER LOCK |");
        static const std::string line(head.size(), '-');
        std::cout << line << std::endl << head << std::endl << line << std::endl;

        for(mem_iter iter = memory_maps[device].begin();
            iter != memory_maps[device].end(); ++iter) {

            std::string status_af("Unknown");
            std::string status_us("Unknown");

            if(!(iter->second.is_free))    status_af = "Yes";
            else                           status_af = " No";

            if((iter->second.is_unlinked)) status_us = "Yes";
            else                           status_us = " No";

            std::string unit = "KB";
            double size = (double)(iter->second.bytes) / 1024;
            if(size >= 1024) {
                size = size / 1024;
                unit = "MB";
            }

            std::cout << "|  " << std::right << std::setw(14) << iter->first << " "
                      << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit
                      << " | " << std::setw(9) << status_af
                      << " | " << std::setw(9) << status_us
                      << " |"  << std::endl;
        }

        std::cout << line << std::endl;
    }

    cl::Buffer *bufferAlloc(const size_t &bytes)
    {
        int n = getActiveDeviceId();
        cl::Buffer *ptr = NULL;
        size_t alloc_bytes = divup(bytes, memory_resolution) * memory_resolution;

        if (bytes > 0) {

            // FIXME: Add better checks for garbage collection
            // Perhaps look at total memory available as a metric
            if (memory_maps[n].size() >= MAX_BUFFERS || used_bytes[n] >= MAX_BYTES) {
                garbageCollect();
            }

            for(mem_iter iter = memory_maps[n].begin();
                iter != memory_maps[n].end(); ++iter) {

                mem_info info = iter->second;

                if ( info.is_free &&
                    !info.is_unlinked &&
                     info.bytes == alloc_bytes) {

                    iter->second.is_free = false;
                    used_bytes[n] += alloc_bytes;
                    used_buffers[n]++;
                    return iter->first;
                }
            }

            try {
                ptr = new cl::Buffer(getContext(), CL_MEM_READ_WRITE, alloc_bytes);
            } catch(...) {
                garbageCollect();
                ptr = new cl::Buffer(getContext(), CL_MEM_READ_WRITE, alloc_bytes);
            }

            mem_info info = {false, false, alloc_bytes};
            memory_maps[n][ptr] = info;
            used_bytes[n] += alloc_bytes;
            used_buffers[n]++;
            total_bytes[n] += alloc_bytes;
        }
        return ptr;
    }

    void bufferFree(cl::Buffer *ptr)
    {
        bufferFreeUnlinked(ptr, false);
    }

    void bufferFreeUnlinked(cl::Buffer *ptr, bool free_unlinked)
    {
        int n = getActiveDeviceId();
        mem_iter iter = memory_maps[n].find(ptr);

        if (iter != memory_maps[n].end()) {

            iter->second.is_free = true;
            if ((iter->second).is_unlinked && !free_unlinked) return;

            iter->second.is_unlinked = false;

            used_bytes[n] -= iter->second.bytes;
            used_buffers[n]--;
        } else {
            destroy(ptr); // Free it because we are not sure what the size is
        }
    }

    void bufferPop(cl::Buffer *ptr)
    {
        int n = getActiveDeviceId();
        mem_iter iter = memory_maps[n].find(ptr);

        if (iter != memory_maps[n].end()) {
            iter->second.is_unlinked = true;
        } else {

            mem_info info = { false,
                              false,
                              100 }; //This number is not relevant

            memory_maps[n][ptr] = info;
        }
    }

    void bufferPush(cl::Buffer *ptr)
    {
        int n = getActiveDeviceId();
        mem_iter iter = memory_maps[n].find(ptr);

        if (iter != memory_maps[n].end()) {
            iter->second.is_unlinked = false;
        }
    }

    void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers,
                          size_t *lock_bytes,  size_t *lock_buffers)
    {
        int n = getActiveDeviceId();
        if (alloc_bytes   ) *alloc_bytes   = total_bytes[n];
        if (alloc_buffers ) *alloc_buffers = memory_maps[n].size();
        if (lock_bytes    ) *lock_bytes    = used_bytes[n];
        if (lock_buffers  ) *lock_buffers  = used_buffers[n];
    }

    template<typename T>
    T *memAlloc(const size_t &elements)
    {
        managerInit();
        return (T *)bufferAlloc(elements * sizeof(T));
    }

    template<typename T>
    void memFree(T *ptr)
    {
        return bufferFreeUnlinked((cl::Buffer *)ptr, false);
    }

    template<typename T>
    void memFreeUnlinked(T *ptr, bool free_unlinked)
    {
        return bufferFreeUnlinked((cl::Buffer *)ptr, free_unlinked);
    }

    template<typename T>
    void memPop(const T *ptr)
    {
        return bufferPop((cl::Buffer *)ptr);
    }

    template<typename T>
    void memPush(const T *ptr)
    {
        return bufferPush((cl::Buffer *)ptr);
    }

    // pinned memory manager
    typedef struct {
        cl::Buffer *buf;
        mem_info info;
    } pinned_info;

    typedef std::map<void*, pinned_info> pinned_t;
    typedef pinned_t::iterator pinned_iter;
    pinned_t pinned_maps[DeviceManager::MAX_DEVICES];
    static size_t pinned_used_bytes = 0;

    static void pinnedDestroy(cl::Buffer *buf, void *ptr)
    {
        getQueue().enqueueUnmapMemObject(*buf, (void *)ptr);
        destroy(buf);
    }

    void pinnedGarbageCollect()
    {
        int n = getActiveDeviceId();
        for(auto &iter : pinned_maps[n]) {
            if ((iter.second).info.is_free) {
                pinnedDestroy(iter.second.buf, iter.first);
            }
        }

        pinned_iter memory_curr = pinned_maps[n].begin();
        pinned_iter memory_end  = pinned_maps[n].end();

        while(memory_curr != memory_end) {
            if (memory_curr->second.info.is_free) {
                memory_curr = pinned_maps[n].erase(memory_curr);
            } else {
                ++memory_curr;
            }
        }

    }

    void *pinnedBufferAlloc(const size_t &bytes)
    {
        void *ptr = NULL;
        int n = getActiveDeviceId();
        // Allocate the higher megabyte. Overhead of creating pinned memory is
        // more so we want more resuable memory.
        size_t alloc_bytes = divup(bytes, 1048576) * 1048576;

        if (bytes > 0) {
            cl::Buffer *buf = NULL;

            // FIXME: Add better checks for garbage collection
            // Perhaps look at total memory available as a metric
            if (pinned_maps[n].size() >= MAX_BUFFERS || pinned_used_bytes >= MAX_BYTES) {
                pinnedGarbageCollect();
            }

            for(pinned_iter iter = pinned_maps[n].begin();
                iter != pinned_maps[n].end(); ++iter) {

                mem_info info = iter->second.info;
                if (info.is_free && info.bytes == alloc_bytes) {
                    iter->second.info.is_free = false;
                    pinned_used_bytes += alloc_bytes;
                    return iter->first;
                }
            }

            try {
                buf = new cl::Buffer(getContext(), CL_MEM_ALLOC_HOST_PTR, alloc_bytes);

                ptr = getQueue().enqueueMapBuffer(*buf, true, CL_MAP_READ|CL_MAP_WRITE,
                                                  0, alloc_bytes);
            } catch(...) {
                pinnedGarbageCollect();
                buf = new cl::Buffer(getContext(), CL_MEM_ALLOC_HOST_PTR, alloc_bytes);

                ptr = getQueue().enqueueMapBuffer(*buf, true, CL_MAP_READ|CL_MAP_WRITE,
                                                  0, alloc_bytes);
            }
            mem_info info = {false, false, alloc_bytes};
            pinned_info pt = {buf, info};
            pinned_maps[n][ptr] = pt;
            pinned_used_bytes += alloc_bytes;
        }
        return ptr;
    }

    void pinnedBufferFree(void *ptr)
    {
        int n = getActiveDeviceId();
        pinned_iter iter = pinned_maps[n].find(ptr);

        if (iter != pinned_maps[n].end()) {
            iter->second.info.is_free = true;
            pinned_used_bytes -= iter->second.info.bytes;
        } else {
            pinnedDestroy(iter->second.buf, ptr); // Free it because we are not sure what the size is
            pinned_maps[n].erase(iter);
        }
    }

    template<typename T>
    T* pinnedAlloc(const size_t &elements)
    {
        managerInit();
        return (T *)pinnedBufferAlloc(elements * sizeof(T));
    }

    template<typename T>
    void pinnedFree(T* ptr)
    {
        return pinnedBufferFree((void *) ptr);
    }

#define INSTANTIATE(T)                                          \
    template T* memAlloc(const size_t &elements);               \
    template void memFree(T* ptr);                              \
    template void memFreeUnlinked(T* ptr, bool free_unlinked);  \
    template void memPop(const T* ptr);                         \
    template void memPush(const T* ptr);                        \
    template T* pinnedAlloc(const size_t &elements);            \
    template void pinnedFree(T* ptr);                           \

    INSTANTIATE(float)
    INSTANTIATE(cfloat)
    INSTANTIATE(double)
    INSTANTIATE(cdouble)
    INSTANTIATE(int)
    INSTANTIATE(uint)
    INSTANTIATE(char)
    INSTANTIATE(uchar)
    INSTANTIATE(intl)
    INSTANTIATE(uintl)
    INSTANTIATE(short)
    INSTANTIATE(ushort)
}
<commit_msg>Renamed is_free -> mngr_lock and is_unlinked -> user_lock in opencl memory mngr<commit_after>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <memory.hpp>
#include <dispatch.hpp>
#include <map>
#include <iostream>
#include <iomanip>
#include <string>
#include <types.hpp>

namespace opencl
{
    static size_t memory_resolution = 1024; //1KB

    void setMemStepSize(size_t step_bytes)
    {
        memory_resolution = step_bytes;
    }

    size_t getMemStepSize(void)
    {
        return memory_resolution;
    }

    // Manager Class
    // Dummy used to call garbage collection at the end of the program
    class Manager
    {
        public:
        static bool initialized;
        Manager()
        {
            initialized = true;
        }

        ~Manager()
        {
            for(int i = 0; i < (int)getDeviceCount(); i++) {
                setDevice(i);
                garbageCollect();
                pinnedGarbageCollect();
            }
        }
    };

    bool Manager::initialized = false;

    static void managerInit()
    {
        if(Manager::initialized == false)
            static Manager pm = Manager();
    }

    typedef struct
    {
        bool mngr_lock;
        bool user_lock;
        size_t bytes;
    } mem_info;

    static size_t used_bytes[DeviceManager::MAX_DEVICES] = {0};
    static size_t used_buffers[DeviceManager::MAX_DEVICES] = {0};
    static size_t total_bytes[DeviceManager::MAX_DEVICES] = {0};

    typedef std::map<cl::Buffer *, mem_info> mem_t;
    typedef mem_t::iterator mem_iter;
    mem_t memory_maps[DeviceManager::MAX_DEVICES];

    static void destroy(cl::Buffer *ptr)
    {
        delete ptr;
    }

    void garbageCollect()
    {
        int n = getActiveDeviceId();
        for(mem_iter iter = memory_maps[n].begin();
            iter != memory_maps[n].end(); ++iter) {

            if (!(iter->second).mngr_lock) {

                if (!(iter->second).user_lock) {
                    destroy(iter->first);
                    total_bytes[n] -= iter->second.bytes;
                }
            }
        }

        mem_iter memory_curr = memory_maps[n].begin();
        mem_iter memory_end  = memory_maps[n].end();

        while(memory_curr != memory_end) {
            if (!memory_curr->second.mngr_lock && !memory_curr->second.user_lock) {
                memory_curr = memory_maps[n].erase(memory_curr);
            } else {
                ++memory_curr;
            }
        }
    }

    void printMemInfo(const char *msg, const int device)
    {
        std::cout << msg << std::endl;
        std::cout << "Memory Map for Device: " << device << std::endl;

        static const std::string head("|     POINTER      |    SIZE    |  AF LOCK  | USER LOCK |");
        static const std::string line(head.size(), '-');
        std::cout << line << std::endl << head << std::endl << line << std::endl;

        for(mem_iter iter = memory_maps[device].begin();
            iter != memory_maps[device].end(); ++iter) {

            std::string status_mngr("Unknown");
            std::string status_user("Unknown");

            if(iter->second.mngr_lock)  status_mngr = "Yes";
            else                        status_mngr = " No";

            if(iter->second.user_lock)  status_user = "Yes";
            else                        status_user = " No";

            std::string unit = "KB";
            double size = (double)(iter->second.bytes) / 1024;
            if(size >= 1024) {
                size = size / 1024;
                unit = "MB";
            }

            std::cout << "|  " << std::right << std::setw(14) << iter->first << " "
                      << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit
                      << " | " << std::setw(9) << status_mngr
                      << " | " << std::setw(9) << status_user
                      << " |"  << std::endl;
        }

        std::cout << line << std::endl;
    }

    cl::Buffer *bufferAlloc(const size_t &bytes)
    {
        int n = getActiveDeviceId();
        cl::Buffer *ptr = NULL;
        size_t alloc_bytes = divup(bytes, memory_resolution) * memory_resolution;

        if (bytes > 0) {

            // FIXME: Add better checks for garbage collection
            // Perhaps look at total memory available as a metric
            if (memory_maps[n].size() >= MAX_BUFFERS || used_bytes[n] >= MAX_BYTES) {
                garbageCollect();
            }

            for(mem_iter iter = memory_maps[n].begin();
                iter != memory_maps[n].end(); ++iter) {

                mem_info info = iter->second;

                if (!info.mngr_lock &&
                    !info.user_lock &&
                     info.bytes == alloc_bytes) {

                    iter->second.mngr_lock = true;
                    used_bytes[n] += alloc_bytes;
                    used_buffers[n]++;
                    return iter->first;
                }
            }

            try {
                ptr = new cl::Buffer(getContext(), CL_MEM_READ_WRITE, alloc_bytes);
            } catch(...) {
                garbageCollect();
                ptr = new cl::Buffer(getContext(), CL_MEM_READ_WRITE, alloc_bytes);
            }

            mem_info info = {true, false, alloc_bytes};
            memory_maps[n][ptr] = info;
            used_bytes[n] += alloc_bytes;
            used_buffers[n]++;
            total_bytes[n] += alloc_bytes;
        }
        return ptr;
    }

    void bufferFree(cl::Buffer *ptr)
    {
        bufferFreeUnlinked(ptr, false);
    }

    void bufferFreeUnlinked(cl::Buffer *ptr, bool free_unlinked)
    {
        int n = getActiveDeviceId();
        mem_iter iter = memory_maps[n].find(ptr);

        if (iter != memory_maps[n].end()) {

            iter->second.mngr_lock = false;
            if ((iter->second).user_lock && !free_unlinked) return;

            iter->second.user_lock = false;

            used_bytes[n] -= iter->second.bytes;
            used_buffers[n]--;
        } else {
            destroy(ptr); // Free it because we are not sure what the size is
        }
    }

    void bufferPop(cl::Buffer *ptr)
    {
        int n = getActiveDeviceId();
        mem_iter iter = memory_maps[n].find(ptr);

        if (iter != memory_maps[n].end()) {
            iter->second.user_lock = true;
        } else {

            mem_info info = { true,
                              true,
                              100 }; //This number is not relevant

            memory_maps[n][ptr] = info;
        }
    }

    void bufferPush(cl::Buffer *ptr)
    {
        int n = getActiveDeviceId();
        mem_iter iter = memory_maps[n].find(ptr);

        if (iter != memory_maps[n].end()) {
            iter->second.user_lock = false;
        }
    }

    void deviceMemoryInfo(size_t *alloc_bytes, size_t *alloc_buffers,
                          size_t *lock_bytes,  size_t *lock_buffers)
    {
        int n = getActiveDeviceId();
        if (alloc_bytes   ) *alloc_bytes   = total_bytes[n];
        if (alloc_buffers ) *alloc_buffers = memory_maps[n].size();
        if (lock_bytes    ) *lock_bytes    = used_bytes[n];
        if (lock_buffers  ) *lock_buffers  = used_buffers[n];
    }

    template<typename T>
    T *memAlloc(const size_t &elements)
    {
        managerInit();
        return (T *)bufferAlloc(elements * sizeof(T));
    }

    template<typename T>
    void memFree(T *ptr)
    {
        return bufferFreeUnlinked((cl::Buffer *)ptr, false);
    }

    template<typename T>
    void memFreeUnlinked(T *ptr, bool free_unlinked)
    {
        return bufferFreeUnlinked((cl::Buffer *)ptr, free_unlinked);
    }

    template<typename T>
    void memPop(const T *ptr)
    {
        return bufferPop((cl::Buffer *)ptr);
    }

    template<typename T>
    void memPush(const T *ptr)
    {
        return bufferPush((cl::Buffer *)ptr);
    }

    // pinned memory manager
    typedef struct {
        cl::Buffer *buf;
        mem_info info;
    } pinned_info;

    typedef std::map<void*, pinned_info> pinned_t;
    typedef pinned_t::iterator pinned_iter;
    pinned_t pinned_maps[DeviceManager::MAX_DEVICES];
    static size_t pinned_used_bytes = 0;

    static void pinnedDestroy(cl::Buffer *buf, void *ptr)
    {
        getQueue().enqueueUnmapMemObject(*buf, (void *)ptr);
        destroy(buf);
    }

    void pinnedGarbageCollect()
    {
        int n = getActiveDeviceId();
        for(auto &iter : pinned_maps[n]) {
            if (!(iter.second).info.mngr_lock) {
                pinnedDestroy(iter.second.buf, iter.first);
            }
        }

        pinned_iter memory_curr = pinned_maps[n].begin();
        pinned_iter memory_end  = pinned_maps[n].end();

        while(memory_curr != memory_end) {
            if (!memory_curr->second.info.mngr_lock) {
                memory_curr = pinned_maps[n].erase(memory_curr);
            } else {
                ++memory_curr;
            }
        }

    }

    void *pinnedBufferAlloc(const size_t &bytes)
    {
        void *ptr = NULL;
        int n = getActiveDeviceId();
        // Allocate the higher megabyte. Overhead of creating pinned memory is
        // more so we want more resuable memory.
        size_t alloc_bytes = divup(bytes, 1048576) * 1048576;

        if (bytes > 0) {
            cl::Buffer *buf = NULL;

            // FIXME: Add better checks for garbage collection
            // Perhaps look at total memory available as a metric
            if (pinned_maps[n].size() >= MAX_BUFFERS || pinned_used_bytes >= MAX_BYTES) {
                pinnedGarbageCollect();
            }

            for(pinned_iter iter = pinned_maps[n].begin();
                iter != pinned_maps[n].end(); ++iter) {

                mem_info info = iter->second.info;
                if (!info.mngr_lock && info.bytes == alloc_bytes) {
                    iter->second.info.mngr_lock = true;
                    pinned_used_bytes += alloc_bytes;
                    return iter->first;
                }
            }

            try {
                buf = new cl::Buffer(getContext(), CL_MEM_ALLOC_HOST_PTR, alloc_bytes);

                ptr = getQueue().enqueueMapBuffer(*buf, true, CL_MAP_READ|CL_MAP_WRITE,
                                                  0, alloc_bytes);
            } catch(...) {
                pinnedGarbageCollect();
                buf = new cl::Buffer(getContext(), CL_MEM_ALLOC_HOST_PTR, alloc_bytes);

                ptr = getQueue().enqueueMapBuffer(*buf, true, CL_MAP_READ|CL_MAP_WRITE,
                                                  0, alloc_bytes);
            }
            mem_info info = {true, false, alloc_bytes};
            pinned_info pt = {buf, info};
            pinned_maps[n][ptr] = pt;
            pinned_used_bytes += alloc_bytes;
        }
        return ptr;
    }

    void pinnedBufferFree(void *ptr)
    {
        int n = getActiveDeviceId();
        pinned_iter iter = pinned_maps[n].find(ptr);

        if (iter != pinned_maps[n].end()) {
            iter->second.info.mngr_lock = false;
            pinned_used_bytes -= iter->second.info.bytes;
        } else {
            pinnedDestroy(iter->second.buf, ptr); // Free it because we are not sure what the size is
            pinned_maps[n].erase(iter);
        }
    }

    template<typename T>
    T* pinnedAlloc(const size_t &elements)
    {
        managerInit();
        return (T *)pinnedBufferAlloc(elements * sizeof(T));
    }

    template<typename T>
    void pinnedFree(T* ptr)
    {
        return pinnedBufferFree((void *) ptr);
    }

#define INSTANTIATE(T)                                          \
    template T* memAlloc(const size_t &elements);               \
    template void memFree(T* ptr);                              \
    template void memFreeUnlinked(T* ptr, bool free_unlinked);  \
    template void memPop(const T* ptr);                         \
    template void memPush(const T* ptr);                        \
    template T* pinnedAlloc(const size_t &elements);            \
    template void pinnedFree(T* ptr);                           \

    INSTANTIATE(float)
    INSTANTIATE(cfloat)
    INSTANTIATE(double)
    INSTANTIATE(cdouble)
    INSTANTIATE(int)
    INSTANTIATE(uint)
    INSTANTIATE(char)
    INSTANTIATE(uchar)
    INSTANTIATE(intl)
    INSTANTIATE(uintl)
    INSTANTIATE(short)
    INSTANTIATE(ushort)
}
<|endoftext|>


<commit_before>
#include <kcombobox.h>
#include <kdebug.h>
#include <klocale.h>
#include <klineedit.h>

#include <konnectormanager.h>
#include <configwidget.h>

#include "konnectorwizard.h"

#include "konnectorprofilewizardintro.h"
#include "konnectorwizardoutro.h"

using namespace KSync;

namespace {

const int AREA = 5210;

void setCurrent( const QString& str, QComboBox* box, bool insert = true )
{
    if ( str.isEmpty() ) return;
    uint b = box->count();
    for ( uint i = 0; i < b; i++ ) {
        if ( box->text(i) == str ) {
            box->setCurrentItem( i );
            return;
        }
    }
    if ( !insert ) return;

    box->insertItem( str );
    box->setCurrentItem( b );
}

}

KonnectorWizard::KonnectorWizard( KonnectorManager *manager )
    : KWizard( 0, "wizard", true ), m_manager( manager )
{
    m_isEdit = false;
    m_conf = 0;
    initUI();
}

KonnectorWizard::KonnectorWizard( KonnectorManager* manager,
                                  const KonnectorProfile &prof )
    : KWizard( 0, "wizard", true ), m_manager( manager)
{
    m_isEdit = true;
    m_kaps = prof.kapabilities();
    m_conf = 0;
    initUI();

    /* init the ui */
    m_outro->lneName->setText( prof.name() );
    kdDebug(AREA) << "Current Identify is " << prof.device().name() << " " << prof.device().identify() << endl;
    setCurrent( prof.device().name(), m_intro->cmbDevice, false );
    kdDebug(AREA) << "Current entry is now  " << m_intro->cmbDevice->currentText() << endl;
    slotKonChanged( m_intro->cmbDevice->currentText() );
}

KonnectorWizard::~KonnectorWizard()
{
}

KonnectorProfile KonnectorWizard::profile() const
{
    KonnectorProfile prof;
    if ( m_conf ) {
        prof.setKapabilities( m_conf->capabilities() );
        prof.setDevice( byString( m_intro->cmbDevice->currentText() ) );
        prof.setName( m_outro->lneName->text() );
    }

    return prof;
}

/*
 * let's add some pages
 * and make the Configure widget be kewl
 * basicly we need to recreate it on
 *
 */
void KonnectorWizard::initUI()
{
    m_conf = 0;
    m_intro = new KonnectorProfileWizardIntro();
    m_outro = new KonnectorWizardOutro();
    addPage( m_intro, "Profile");
    addPage( m_outro, "Outro");
    setFinishEnabled( m_outro, true );

    Device::ValueList list = m_manager->query();
    Device::ValueList::Iterator it;
    m_current =i18n("Please choose a Konnector");
    m_intro->cmbDevice->insertItem( m_current  );
    for (it = list.begin(); it != list.end(); ++it ) {
	kdDebug(AREA) << "Inserting into Combo " << (*it).name() << " " << (*it).identify() << endl;
        m_intro->cmbDevice->insertItem( (*it).name() );
        m_devices.insert( (*it).name(), (*it) );
    }
    // connect to textchanges
    connect(m_intro->cmbDevice, SIGNAL(activated(const QString&) ),
            SLOT(slotKonChanged(const QString&) ) );
}

/*
 * If the Device Combobox changed we need to update the
 * Configuration Tab
 * First check tif the selection changed
 * Then check if the selection changed to the Kapabilities
 * giving as parameter
 * then recreate the widget
 */
void KonnectorWizard::slotKonChanged( const QString& str)
{
    if ( str == m_current )  // the selection was not changed
        return;
    if ( str == i18n("Please choose a Konnector") ) {
        kdDebug(5210) << "Connector " << endl;
        delete m_conf;
        m_conf =0;
        return;
    }
    m_current = str;

    delete m_conf;
    m_conf = 0;

    Device dev = byString( str );

    // load the Konnector for getting a ConfigureWidget
    Konnector *k = m_manager->load( dev );
    if ( !k ) return;

    if( !m_isEdit )
        m_conf = m_manager->configWidget( k, this, "config" ); // never 0
    else
        m_conf = m_manager->configWidget( k, m_kaps, this, "config" );

    insertPage(m_conf, i18n("Configure"), 1 );
    m_manager->unload( k );
}

Device KonnectorWizard::byString( const QString& str ) const
{
    return m_devices[str];
}

#include "konnectorwizard.moc"
<commit_msg>Fix creation and editing of Konnectors which don't have a config widget.<commit_after>
#include <kcombobox.h>
#include <kdebug.h>
#include <klocale.h>
#include <klineedit.h>

#include <konnectormanager.h>
#include <configwidget.h>

#include "konnectorwizard.h"

#include "konnectorprofilewizardintro.h"
#include "konnectorwizardoutro.h"

using namespace KSync;

namespace {

const int AREA = 5210;

void setCurrent( const QString& str, QComboBox* box, bool insert = true )
{
    if ( str.isEmpty() ) return;
    uint b = box->count();
    for ( uint i = 0; i < b; i++ ) {
        if ( box->text(i) == str ) {
            box->setCurrentItem( i );
            return;
        }
    }
    if ( !insert ) return;

    box->insertItem( str );
    box->setCurrentItem( b );
}

}

KonnectorWizard::KonnectorWizard( KonnectorManager *manager )
    : KWizard( 0, "wizard", true ), m_manager( manager )
{
    m_isEdit = false;
    m_conf = 0;
    initUI();
}

KonnectorWizard::KonnectorWizard( KonnectorManager* manager,
                                  const KonnectorProfile &prof )
    : KWizard( 0, "wizard", true ), m_manager( manager)
{
    m_isEdit = true;
    m_kaps = prof.kapabilities();
    m_conf = 0;
    initUI();

    /* init the ui */
    m_outro->lneName->setText( prof.name() );
    kdDebug(AREA) << "Current Identify is " << prof.device().name() << " " << prof.device().identify() << endl;
    setCurrent( prof.device().name(), m_intro->cmbDevice, false );
    kdDebug(AREA) << "Current entry is now  " << m_intro->cmbDevice->currentText() << endl;
    slotKonChanged( m_intro->cmbDevice->currentText() );
}

KonnectorWizard::~KonnectorWizard()
{
}

KonnectorProfile KonnectorWizard::profile() const
{
    KonnectorProfile prof;

    if ( m_conf ) {
        prof.setKapabilities( m_conf->capabilities() );
    }

    prof.setDevice( byString( m_intro->cmbDevice->currentText() ) );
    prof.setName( m_outro->lneName->text() );

    return prof;
}

/*
 * let's add some pages
 * and make the Configure widget be kewl
 * basicly we need to recreate it on
 *
 */
void KonnectorWizard::initUI()
{
    m_conf = 0;
    m_intro = new KonnectorProfileWizardIntro();
    m_outro = new KonnectorWizardOutro();
    addPage( m_intro, "Profile");
    addPage( m_outro, "Outro");
    setFinishEnabled( m_outro, true );

    Device::ValueList list = m_manager->query();
    Device::ValueList::Iterator it;
    m_current =i18n("Please choose a Konnector");
    m_intro->cmbDevice->insertItem( m_current  );
    for (it = list.begin(); it != list.end(); ++it ) {
	kdDebug(AREA) << "Inserting into Combo " << (*it).name() << " " << (*it).identify() << endl;
        m_intro->cmbDevice->insertItem( (*it).name() );
        m_devices.insert( (*it).name(), (*it) );
    }
    // connect to textchanges
    connect(m_intro->cmbDevice, SIGNAL(activated(const QString&) ),
            SLOT(slotKonChanged(const QString&) ) );
}

/*
 * If the Device Combobox changed we need to update the
 * Configuration Tab
 * First check tif the selection changed
 * Then check if the selection changed to the Kapabilities
 * giving as parameter
 * then recreate the widget
 */
void KonnectorWizard::slotKonChanged( const QString& str)
{
    if ( str == m_current )  // the selection was not changed
        return;
    if ( str == i18n("Please choose a Konnector") ) {
        kdDebug(5210) << "Connector " << endl;
        delete m_conf;
        m_conf =0;
        return;
    }
    m_current = str;

    delete m_conf;
    m_conf = 0;

    Device dev = byString( str );

    // load the Konnector for getting a ConfigureWidget
    Konnector *k = m_manager->load( dev );
    if ( !k ) return;

    if( !m_isEdit )
        m_conf = m_manager->configWidget( k, this, "config" ); // never 0
    else
        m_conf = m_manager->configWidget( k, m_kaps, this, "config" );

    insertPage(m_conf, i18n("Configure"), 1 );
    m_manager->unload( k );
}

Device KonnectorWizard::byString( const QString& str ) const
{
    return m_devices[str];
}

#include "konnectorwizard.moc"
<|endoftext|>