<commit_before>#include "unittest.h"

#include "main/TurboPumpApp.h"

#include "command_line/CommandLine.h"
#include "serialize/StringUtil.h"
#include "time/Timer.h"
#include <array>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <thread>

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
using std::shared_ptr;
using std::string;

class TurboRunner
{
public:
	TurboRunner(TurboPumpApp& worker)
		: _worker(worker)
		, _thread(std::bind(&TurboPumpApp::run, &worker))
	{}

	~TurboRunner()
	{
		_worker.shutdown();
		_thread.join();
	}

protected:
	TurboPumpApp& _worker;
	std::thread _thread;
};

namespace {
	void createMemberFile()
	{
		Membership membership("turbo_members.txt", "localhost:1337");
		membership.add("one");
		membership.addIp("127.0.0.1:9001", "one");
		membership.add("two");
		membership.addIp("127.0.0.1:9002", "two");
		membership.save();
	}

	int openStreamSocket(const string& where)
	{
		struct sockaddr_un address;
		memset(&address, 0, sizeof(struct sockaddr_un));
		address.sun_family = AF_UNIX;
		snprintf(address.sun_path, where.size()+1, where.c_str());

		int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
		assertTrue( socket_fd >= 0 );
		assertTrue( connect(socket_fd, (struct sockaddr*)&address, sizeof(struct sockaddr_un)) == 0 );

		return socket_fd;
	}
}

TEST_CASE( "StartupTest/testMerkleHealing", "[integration]" )
{
	createMemberFile();

	TurboApi api;
	api.options.write_chaining = false;
	TurboPumpApp workerOne(api, "/tmp/workerOne", 9001);
	TurboRunner runnerOne(workerOne);

	TurboPumpApp workerTwo(api, "/tmp/workerTwo", 9002);
	TurboRunner runnerTwo(workerTwo);

	// need some events to signal when we're running...
	CommandLine::run("sleep 1");

	// test stuff!

	string response = CommandLine::run("echo 'membership||' | nc -U /tmp/workerOne");
	assertEquals( "one 127.0.0.1:9001\n"
				  "two 127.0.0.1:9002", response );

	for (unsigned i = 0; i < 5; ++i)
	{
		string num = StringUtil::str(i);
		response = CommandLine::run("echo 'write|name=one" + num + "|one hello" + num + "' | nc -U /tmp/workerOne");
		assertEquals( "", response );
	}
	for (unsigned i = 0; i < 5; ++i)
	{
		string num = StringUtil::str(i);
		response = CommandLine::run("echo 'write|name=two" + num + "|two hello" + num + "' | nc -U /tmp/workerTwo");
		assertEquals( "", response );
	}

	CommandLine::run("sleep 10");

	string expected = "(one0)=>11\n"
					  "(one1)=>11\n"
					  "(one2)=>11\n"
					  "(one3)=>11\n"
					  "(one4)=>11\n"
					  "(two0)=>11\n"
					  "(two1)=>11\n"
					  "(two2)=>11\n"
					  "(two3)=>11\n"
					  "(two4)=>11";

	response = CommandLine::run("echo 'local_list||' | nc -U /tmp/workerTwo");
	assertEquals( expected, response );

	response = CommandLine::run("echo 'local_list||' | nc -U /tmp/workerOne");
	assertEquals( expected, response );
}

namespace
{
	class Checkpoint
	{
	public:
		Checkpoint() {}

		void reset()
		{
			_timer.reset();
		}

		void add()
		{
			_results.push_back(_timer.micros());
		}

		const Timer& timer() const
		{
			return _timer;
		}

		const std::vector<long long>& results() const
		{
			return _results;
		}

	protected:
		Timer _timer;
		std::vector<long long> _results;
	};
}

TEST_CASE( "StartupTest/testWriteChaining", "[integration]" )
{
	const unsigned numFiles = 5;
	createMemberFile();

	TurboApi api;
	api.options.merkle = false;

	// create all checkpoints while we're on an orderly single thread.
	// the callbacks will be chaotic, but as long as all our map allocations are done, we should be fine
	std::map<string,Checkpoint> checkpoints;
	for (unsigned i = 0; i < numFiles; ++i)
		checkpoints[StringUtil::str(i)];

	api.when_local_write_finishes = api.when_mirror_write_finishes = [&checkpoints] (std::string filename, IDataStoreReader::ptr)
	{
		checkpoints[filename].add();
	};

	TurboPumpApp workerOne(api, "/tmp/workerOne", 9001);
	TurboRunner runnerOne(workerOne);

	TurboPumpApp workerTwo(api, "/tmp/workerTwo", 9002);
	TurboRunner runnerTwo(workerTwo);

	// need some events to signal when we're running...
	CommandLine::run("sleep 1");

	// test stuff!
	CommandLine::run("echo 'write|name=primer|priming the wan pump' | nc -U /tmp/workerOne");
	CommandLine::run("sleep 1");

	for (unsigned i = 0; i < numFiles; ++i)
	{
		string num = StringUtil::str(i);
		checkpoints[num].reset();

		string packet = "write|name=" + num + "|hello" + num + "\n";

		int socket_fd = openStreamSocket("/tmp/workerOne");
		size_t bytesWrit = write(socket_fd, packet.data(), packet.size());
		close(socket_fd);

		std::cout << "local write " << i <<  " finished at " << checkpoints[num].timer().micros() << "us" << std::endl;
	}

	string expected = "(0)=>7\n"
			"(1)=>7\n"
			"(2)=>7\n"
			"(3)=>7\n"
			"(4)=>7\n"
			"(primer)=>21";

	string response;
	Timer t;
	while (t.millis() < 5000)
	{
		response = CommandLine::run("echo 'local_list||' | nc -U /tmp/workerTwo");
		if (response == expected)
			break;
		CommandLine::run("sleep 1");
	}
	assertEquals( expected, response );

	for (std::map<string,Checkpoint>::const_iterator it = checkpoints.begin(); it != checkpoints.end(); ++it)
		std::cout << "  " << it->first << " : " << StringUtil::stlJoin(it->second.results()) << std::endl;
}

<commit_msg>More research on UDT's epoll_wait.<commit_after>#include "unittest.h"

#include "main/TurboPumpApp.h"

#include "command_line/CommandLine.h"
#include "serialize/StringUtil.h"
#include "time/Timer.h"
#include <array>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <thread>

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
using std::shared_ptr;
using std::string;

class TurboRunner
{
public:
	TurboRunner(TurboPumpApp& worker)
		: _worker(worker)
		, _thread(std::bind(&TurboPumpApp::run, &worker))
	{}

	~TurboRunner()
	{
		_worker.shutdown();
		_thread.join();
	}

protected:
	TurboPumpApp& _worker;
	std::thread _thread;
};

namespace {
	void createMemberFile()
	{
		Membership membership("turbo_members.txt", "localhost:1337");
		membership.add("one");
		membership.addIp("127.0.0.1:9001", "one");
		membership.add("two");
		membership.addIp("127.0.0.1:9002", "two");
		membership.save();
	}

	int openStreamSocket(const string& where)
	{
		struct sockaddr_un address;
		memset(&address, 0, sizeof(struct sockaddr_un));
		address.sun_family = AF_UNIX;
		snprintf(address.sun_path, where.size()+1, where.c_str());

		int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
		assertTrue( socket_fd >= 0 );
		assertTrue( connect(socket_fd, (struct sockaddr*)&address, sizeof(struct sockaddr_un)) == 0 );

		return socket_fd;
	}
}

TEST_CASE( "StartupTest/testMerkleHealing", "[integration]" )
{
	createMemberFile();

	TurboApi api;
	api.options.write_chaining = false;
	TurboPumpApp workerOne(api, "/tmp/workerOne", 9001);
	TurboRunner runnerOne(workerOne);

	TurboPumpApp workerTwo(api, "/tmp/workerTwo", 9002);
	TurboRunner runnerTwo(workerTwo);

	// need some events to signal when we're running...
	CommandLine::run("sleep 1");

	// test stuff!

	string response = CommandLine::run("echo 'membership||' | nc -U /tmp/workerOne");
	assertEquals( "one 127.0.0.1:9001\n"
				  "two 127.0.0.1:9002", response );

	for (unsigned i = 0; i < 5; ++i)
	{
		string num = StringUtil::str(i);
		response = CommandLine::run("echo 'write|name=one" + num + "|one hello" + num + "' | nc -U /tmp/workerOne");
		assertEquals( "", response );
	}
	for (unsigned i = 0; i < 5; ++i)
	{
		string num = StringUtil::str(i);
		response = CommandLine::run("echo 'write|name=two" + num + "|two hello" + num + "' | nc -U /tmp/workerTwo");
		assertEquals( "", response );
	}

	CommandLine::run("sleep 10");

	string expected = "(one0)=>11\n"
					  "(one1)=>11\n"
					  "(one2)=>11\n"
					  "(one3)=>11\n"
					  "(one4)=>11\n"
					  "(two0)=>11\n"
					  "(two1)=>11\n"
					  "(two2)=>11\n"
					  "(two3)=>11\n"
					  "(two4)=>11";

	response = CommandLine::run("echo 'local_list||' | nc -U /tmp/workerTwo");
	assertEquals( expected, response );

	response = CommandLine::run("echo 'local_list||' | nc -U /tmp/workerOne");
	assertEquals( expected, response );
}

namespace
{
	class Checkpoint
	{
	public:
		Checkpoint() {}

		void reset()
		{
			_timer.reset();
		}

		void add()
		{
			_results.push_back(_timer.micros());
		}

		const Timer& timer() const
		{
			return _timer;
		}

		const std::vector<long long>& results() const
		{
			return _results;
		}

	protected:
		Timer _timer;
		std::vector<long long> _results;
	};
}

TEST_CASE( "StartupTest/testWriteChaining", "[integration]" )
{
	const unsigned numFiles = 5;
	createMemberFile();

	TurboApi api;
	api.options.merkle = false;

	// create all checkpoints while we're on an orderly single thread.
	// the callbacks will be chaotic, but as long as all our map allocations are done, we should be fine
	std::map<string,Checkpoint> checkpoints;
	for (unsigned i = 0; i < numFiles; ++i)
		checkpoints[StringUtil::str(i)];

	api.when_local_write_finishes = api.when_mirror_write_finishes = [&checkpoints] (std::string filename, IDataStoreReader::ptr)
	{
		checkpoints[filename].add();
	};

	TurboPumpApp workerOne(api, "/tmp/workerOne", 9001);
	TurboRunner runnerOne(workerOne);

	TurboPumpApp workerTwo(api, "/tmp/workerTwo", 9002);
	TurboRunner runnerTwo(workerTwo);

	// need some events to signal when we're running...
	CommandLine::run("sleep 1");

	// test stuff!
	CommandLine::run("echo 'write|name=primer|priming the wan pump' | nc -U /tmp/workerOne");
	CommandLine::run("sleep 0.2");

	for (unsigned i = 0; i < numFiles; ++i)
	{
		string num = StringUtil::str(i);
		checkpoints[num].reset();

		string packet = "write|name=" + num + "|hello" + num + "\n";

		int socket_fd = openStreamSocket("/tmp/workerOne");
		size_t bytesWrit = write(socket_fd, packet.data(), packet.size());
		close(socket_fd);

		std::cout << "local write " << i <<  " finished at " << checkpoints[num].timer().micros() << "us" << std::endl;
	}

	string expected = "(0)=>7\n"
			"(1)=>7\n"
			"(2)=>7\n"
			"(3)=>7\n"
			"(4)=>7\n"
			"(primer)=>21";

	string response;
	Timer t;
	while (t.millis() < 5000)
	{
		response = CommandLine::run("echo 'local_list||' | nc -U /tmp/workerTwo");
		if (response == expected)
			break;
		CommandLine::run("sleep 1");
	}
	assertEquals( expected, response );

	for (std::map<string,Checkpoint>::const_iterator it = checkpoints.begin(); it != checkpoints.end(); ++it)
		std::cout << "  " << it->first << " : " << StringUtil::stlJoin(it->second.results()) << std::endl;
}

<|endoftext|>


<commit_before>#include "RThread.h"
#include "REventLoop.h"
#include "RForwardList.h"
#include "RSpinLocker.h"

class RThreadPrivate
{
public:
  static void
  run(void *arg);
};

// FIXME: Thread list not guarded by mutex now !
static RForwardList<RThread *> sThreads;
void
RThreadPrivate::run(void *arg)
{
  RThread *thread = static_cast<RThread *>(arg);

  if(thread)
  {
    thread->started.emit();
    thread->run();
    thread->finished.emit();
  }

  // We must delete self when wan't to exit a task.
  // Reference to "Implementing a Task" section of FreeRTOS.
  vTaskDelete(NULL);
}

RThread::RThread(TaskHandle_t handle)
// So that RObject won't create another RThread lead infinite looping
  : RObject(this)
  , mStackSize(128)
  , mHandle(handle)
  , mIsOwnded(false)
{
}

RThread::RThread()
  : RObject(this)
  , mStackSize(128)
  , mHandle(NULL)
  , mIsOwnded(false)
{
  R_MAKE_SPINLOCKER();
  sThreads.pushFront(this);
}

RThread::~RThread()
{
  terminate();

  R_MAKE_SPINLOCKER();

  // Clear this thread pointer in thread list
  sThreads.remove(this);
}

void
RThread::exit(int returnCode)
{
  REventLoop::instance(this)->exit(returnCode);
}

bool
RThread::isFinished() const
{
  if(!mHandle)
  {
    return true;
  }

  return (eDeleted == eTaskGetState(mHandle));
}

bool
RThread::isRunning() const
{
  if(!mHandle)
  {
    return false;
  }

  return (eDeleted != eTaskGetState(mHandle));
}

RThread::Priority
RThread::priority() const
{
  if(!mHandle)
  {
    return IdlePriority;
  }

  return static_cast<RThread::Priority>(uxTaskPriorityGet(mHandle));
}

void
RThread::setPriority(RThread::Priority priority)
{
  if(!mHandle)
  {
    return;
  }

  vTaskPrioritySet(mHandle, static_cast<UBaseType_t>(priority));
}

void
RThread::setStackSize(size_t stackSize)
{
  mStackSize = stackSize;
}

size_t
RThread::stackSize() const
{
  return mStackSize;
}

bool
RThread::wait(unsigned long time)
{
  /// FIXME: Ugly way to wait for thread finished.

  /* Block for 100ms each time */
  const TickType_t ticksPerBlock = 100 / portTICK_PERIOD_MS;

  if(currentThreadId() == mHandle)
  {
    // You can't wait yourself in the samethread!
    return false;
  }

  while(true)
  {
    if(isFinished())
    {
      return true;
    }

    if(time < std::numeric_limits<unsigned long>::max())
    {
      if(time <= 100)
      {
        // Timeout!
        return false;
      }
      else
      {
        time -= 100;
      }
    }

    vTaskDelay(ticksPerBlock);
  }
}

void
RThread::quit()
{
  this->exit(0);
}

void
RThread::start(RThread::Priority priority)
{
  /* Create the task, storing the handle. */
  TaskHandle_t handle = NULL;

  if(isRunning())
  {
    return;
  }

  terminate();

  if(InheritPriority == priority)
  {
    priority =
      static_cast<RThread::Priority>(uxTaskPriorityGet(currentThreadId()));
  }

  auto ret = xTaskCreate(
    RThreadPrivate::run,  /* Function that implements the task. */
    "",                /* Text name for the task. */
    (stackSize() + 1) / sizeof(uint16_t), /* Stack size in words, not bytes. */
    static_cast<void *>(this),      /* Parameter passed into the task. */
    priority,  /* Priority at which the task is created. */
    &handle);   /* Used to pass out the created task's handle. */

  if(ret == pdPASS)
  {
    mIsOwnded = true;
    mHandle   = handle;
  }
}

void
RThread::terminate()
{
  if(mIsOwnded && mHandle)
  {
    // Ensure no tasks and interrupts will be trigger during thread event loop
    // destruction.
    {
      R_MAKE_SPINLOCKER();
      REventLoop::_destroy(this);
      vTaskDelete(mHandle);
    }

    terminated.emit();
  }

  mHandle = NULL;
}

RThread *
RThread::currentThread()
{
  TaskHandle_t handle = currentThreadId();

  for(auto it = sThreads.begin(); it != sThreads.end(); ++it)
  {
    if(handle == (*it)->mHandle)
    {
      return *it;
    }
  }

  // FIXME: Here generated an orphan RThread object!
  return new RThread(handle);
}

TaskHandle_t
RThread::currentThreadId()
{
  return xTaskGetCurrentTaskHandle();
}

void
RThread::yieldCurrentThread()
{
  taskYIELD();
}

int
RThread::exec()
{
  REventLoop *loop = REventLoop::instance(this);

  return loop->exec();
}

void
RThread::run()
{
  exec();
}

void
RThread::msleep(unsigned long msecs)
{
  unsigned long blockTime = std::numeric_limits<unsigned long>::max() / 1000;

  while(msecs > 0)
  {
    if(msecs <= std::numeric_limits<unsigned long>::max())
    {
      usleep(msecs);
      return;
    }

    usleep(std::numeric_limits<unsigned long>::max());
    msecs -= blockTime;
  }
}

void
RThread::sleep(unsigned long secs)
{
  unsigned long blockTime = std::numeric_limits<unsigned long>::max() /
                            1000000l;

  while(secs > 0)
  {
    if(secs <= std::numeric_limits<unsigned long>::max())
    {
      usleep(secs);
      return;
    }

    usleep(std::numeric_limits<unsigned long>::max());
    secs -= blockTime;
  }
}

void
RThread::usleep(unsigned long usecs)
{
  unsigned long ticks = usecs / (1000 * portTICK_PERIOD_MS);

  while(ticks > 0)
  {
    if(ticks <= static_cast<decltype(usecs)>(portMAX_DELAY))
    {
      vTaskDelay(static_cast<decltype(portMAX_DELAY)>(ticks));
      return;
    }

    vTaskDelay(portMAX_DELAY);
    ticks -= static_cast<decltype(usecs)>(portMAX_DELAY);
  }
}
<commit_msg>Fixed sleep methods not work during setup()<commit_after>#include "RThread.h"
#include "REventLoop.h"
#include "RForwardList.h"
#include "RSpinLocker.h"

class RThreadPrivate
{
public:
  static void
  run(void *arg);
};

// FIXME: Thread list not guarded by mutex now !
static RForwardList<RThread *> sThreads;
void
RThreadPrivate::run(void *arg)
{
  RThread *thread = static_cast<RThread *>(arg);

  if(thread)
  {
    thread->started.emit();
    thread->run();
    thread->finished.emit();
  }

  // We must delete self when wan't to exit a task.
  // Reference to "Implementing a Task" section of FreeRTOS.
  vTaskDelete(NULL);
}

RThread::RThread(TaskHandle_t handle)
// So that RObject won't create another RThread lead infinite looping
  : RObject(this)
  , mStackSize(128)
  , mHandle(handle)
  , mIsOwnded(false)
{
}

RThread::RThread()
  : RObject(this)
  , mStackSize(128)
  , mHandle(NULL)
  , mIsOwnded(false)
{
  R_MAKE_SPINLOCKER();
  sThreads.pushFront(this);
}

RThread::~RThread()
{
  terminate();

  R_MAKE_SPINLOCKER();

  // Clear this thread pointer in thread list
  sThreads.remove(this);
}

void
RThread::exit(int returnCode)
{
  REventLoop::instance(this)->exit(returnCode);
}

bool
RThread::isFinished() const
{
  if(!mHandle)
  {
    return true;
  }

  return (eDeleted == eTaskGetState(mHandle));
}

bool
RThread::isRunning() const
{
  if(!mHandle)
  {
    return false;
  }

  return (eDeleted != eTaskGetState(mHandle));
}

RThread::Priority
RThread::priority() const
{
  if(!mHandle)
  {
    return IdlePriority;
  }

  return static_cast<RThread::Priority>(uxTaskPriorityGet(mHandle));
}

void
RThread::setPriority(RThread::Priority priority)
{
  if(!mHandle)
  {
    return;
  }

  vTaskPrioritySet(mHandle, static_cast<UBaseType_t>(priority));
}

void
RThread::setStackSize(size_t stackSize)
{
  mStackSize = stackSize;
}

size_t
RThread::stackSize() const
{
  return mStackSize;
}

bool
RThread::wait(unsigned long time)
{
  /// FIXME: Ugly way to wait for thread finished.

  /* Block for 100ms each time */
  const TickType_t ticksPerBlock = 100 / portTICK_PERIOD_MS;

  if(currentThreadId() == mHandle)
  {
    // You can't wait yourself in the samethread!
    return false;
  }

  while(true)
  {
    if(isFinished())
    {
      return true;
    }

    if(time < std::numeric_limits<unsigned long>::max())
    {
      if(time <= 100)
      {
        // Timeout!
        return false;
      }
      else
      {
        time -= 100;
      }
    }

    vTaskDelay(ticksPerBlock);
  }
}

void
RThread::quit()
{
  this->exit(0);
}

void
RThread::start(RThread::Priority priority)
{
  /* Create the task, storing the handle. */
  TaskHandle_t handle = NULL;

  if(isRunning())
  {
    return;
  }

  terminate();

  if(InheritPriority == priority)
  {
    priority =
      static_cast<RThread::Priority>(uxTaskPriorityGet(currentThreadId()));
  }

  auto ret = xTaskCreate(
    RThreadPrivate::run,  /* Function that implements the task. */
    "",                /* Text name for the task. */
    (stackSize() + 1) / sizeof(uint16_t), /* Stack size in words, not bytes. */
    static_cast<void *>(this),      /* Parameter passed into the task. */
    priority,  /* Priority at which the task is created. */
    &handle);   /* Used to pass out the created task's handle. */

  if(ret == pdPASS)
  {
    mIsOwnded = true;
    mHandle   = handle;
  }
}

void
RThread::terminate()
{
  if(mIsOwnded && mHandle)
  {
    // Ensure no tasks and interrupts will be trigger during thread event loop
    // destruction.
    {
      R_MAKE_SPINLOCKER();
      REventLoop::_destroy(this);
      vTaskDelete(mHandle);
    }

    terminated.emit();
  }

  mHandle = NULL;
}

RThread *
RThread::currentThread()
{
  TaskHandle_t handle = currentThreadId();

  for(auto it = sThreads.begin(); it != sThreads.end(); ++it)
  {
    if(handle == (*it)->mHandle)
    {
      return *it;
    }
  }

  // FIXME: Here generated an orphan RThread object!
  return new RThread(handle);
}

TaskHandle_t
RThread::currentThreadId()
{
  return xTaskGetCurrentTaskHandle();
}

void
RThread::yieldCurrentThread()
{
  taskYIELD();
}

int
RThread::exec()
{
  REventLoop *loop = REventLoop::instance(this);

  return loop->exec();
}

void
RThread::run()
{
  exec();
}

void
RThread::msleep(unsigned long msecs)
{
  unsigned long blockMS = std::numeric_limits<unsigned long>::max() / 1000;
  unsigned long blockUS = blockMS * 1000;

  while(msecs > 0)
  {
    if(msecs < blockMS)
    {
      blockMS = msecs;
      blockUS = blockMS * 1000;
    }

    usleep(blockUS);

    msecs -= blockMS;
  }
}

void
RThread::sleep(unsigned long secs)
{
  unsigned long blockS  = std::numeric_limits<unsigned long>::max() / 1000;
  unsigned long blockMS = blockS * 1000;

  while(secs > 0)
  {
    if(secs < blockMS)
    {
      blockS  = secs;
      blockMS = blockS * 1000;
    }

    msleep(blockMS);

    secs -= blockS;
  }
}

void
RThread::usleep(unsigned long usecs)
{
  unsigned long ticks = usecs / (1000 * portTICK_PERIOD_MS);

  auto maxTicks = static_cast<decltype(usecs)>(portMAX_DELAY);

  if(ticks <= 0)
  {
    ticks = 1;
    usecs = portTICK_PERIOD_MS * 1000;
  }

  if(currentThreadId())
  {
    while(ticks > 0)
    {
      if(ticks < maxTicks)
      {
        maxTicks = ticks;
      }

      vTaskDelay(maxTicks);
      ticks -= maxTicks;
    }
  }
  else
  {
    delayMicroseconds(usecs);
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrNonlinearColorSpaceXformEffect.h"
#include "GrColorSpaceXform.h"
#include "GrProcessor.h"
#include "SkColorSpace_Base.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"

class GrGLNonlinearColorSpaceXformEffect : public GrGLSLFragmentProcessor {
public:
    void emitCode(EmitArgs& args) override {
        const GrNonlinearColorSpaceXformEffect& csxe =
                args.fFp.cast<GrNonlinearColorSpaceXformEffect>();
        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

        const char* srcCoeffsName = nullptr;
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
            fSrcTransferFnUni = uniformHandler->addUniformArray(
                    kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
                    "SrcTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                    &srcCoeffsName);
        }

        const char* dstCoeffsName = nullptr;
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
            fDstTransferFnUni = uniformHandler->addUniformArray(
                    kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
                    "DstTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                    &dstCoeffsName);
        }

        const char* gamutXformName = nullptr;
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
            fGamutXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType,
                                                        kDefault_GrSLPrecision, "GamutXform",
                                                        &gamutXformName);
        }

        // Helper function to apply the src or dst transfer function to a single value
        SkString tfFuncNames[2];
        for (size_t i = 0; i < 2; ++i) {
            const char* coeffsName = i ? dstCoeffsName : srcCoeffsName;
            if (!coeffsName) {
                continue;
            }
            const char* fnName = i ? "dst_transfer_fn" : "src_transfer_fn";
            static const GrShaderVar gTransferFnFuncArgs[] = {
                    GrShaderVar("x", kFloat_GrSLType),
            };
            SkString transferFnBody;
            // Temporaries to make evaluation line readable
            transferFnBody.printf("float A = %s[0];", coeffsName);
            transferFnBody.appendf("float B = %s[1];", coeffsName);
            transferFnBody.appendf("float C = %s[2];", coeffsName);
            transferFnBody.appendf("float D = %s[3];", coeffsName);
            transferFnBody.appendf("float E = %s[4];", coeffsName);
            transferFnBody.appendf("float F = %s[5];", coeffsName);
            transferFnBody.appendf("float G = %s[6];", coeffsName);
            transferFnBody.append("float s = sign(x);");
            transferFnBody.append("x = abs(x);");
            transferFnBody.appendf("return s * ((x < D) ? (C * x) + F : pow(A * x + B, G) + E);");
            fragBuilder->emitFunction(kFloat_GrSLType, fnName, SK_ARRAY_COUNT(gTransferFnFuncArgs),
                                      gTransferFnFuncArgs, transferFnBody.c_str(), &tfFuncNames[i]);
        }

        if (nullptr == args.fInputColor) {
            args.fInputColor = "vec4(1)";
        }
        fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);

        // 1: Un-premultiply the input color (if necessary)
        fragBuilder->codeAppendf("float nonZeroAlpha = max(color.a, 0.00001);");
        fragBuilder->codeAppendf("color = vec4(color.rgb / nonZeroAlpha, nonZeroAlpha);");

        // 2: Apply src transfer function (to get to linear RGB)
        if (srcCoeffsName) {
            fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[0].c_str());
            fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[0].c_str());
            fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[0].c_str());
        }

        // 3: Apply gamut matrix
        if (gamutXformName) {
            // Color is unpremultiplied at this point, so clamp to [0, 1]
            fragBuilder->codeAppendf(
                "color.rgb = clamp((%s * vec4(color.rgb, 1.0)).rgb, 0.0, 1.0);", gamutXformName);
        }

        // 4: Apply dst transfer fn
        if (dstCoeffsName) {
            fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[1].c_str());
            fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[1].c_str());
            fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[1].c_str());
        }

        // 5: Premultiply again
        fragBuilder->codeAppendf("%s = vec4(color.rgb * color.a, color.a);", args.fOutputColor);
    }

    static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
                              GrProcessorKeyBuilder* b) {
        const GrNonlinearColorSpaceXformEffect& csxe =
                processor.cast<GrNonlinearColorSpaceXformEffect>();
        b->add32(csxe.ops());
    }

protected:
    void onSetData(const GrGLSLProgramDataManager& pdman,
                   const GrFragmentProcessor& processor) override {
        const GrNonlinearColorSpaceXformEffect& csxe =
                processor.cast<GrNonlinearColorSpaceXformEffect>();
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
            pdman.set1fv(fSrcTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                         csxe.srcTransferFnCoeffs());
        }
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
            pdman.set1fv(fDstTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                         csxe.dstTransferFnCoeffs());
        }
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
            pdman.setSkMatrix44(fGamutXformUni, csxe.gamutXform());
        }
    }

private:
    GrGLSLProgramDataManager::UniformHandle fSrcTransferFnUni;
    GrGLSLProgramDataManager::UniformHandle fDstTransferFnUni;
    GrGLSLProgramDataManager::UniformHandle fGamutXformUni;

    typedef GrGLSLFragmentProcessor INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect(
    uint32_t ops, const SkColorSpaceTransferFn& srcTransferFn,
    const SkColorSpaceTransferFn& dstTransferFn, const SkMatrix44& gamutXform)
        : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
        , fGamutXform(gamutXform)
        , fOps(ops) {
    this->initClassID<GrNonlinearColorSpaceXformEffect>();

    fSrcTransferFnCoeffs[0] = srcTransferFn.fA;
    fSrcTransferFnCoeffs[1] = srcTransferFn.fB;
    fSrcTransferFnCoeffs[2] = srcTransferFn.fC;
    fSrcTransferFnCoeffs[3] = srcTransferFn.fD;
    fSrcTransferFnCoeffs[4] = srcTransferFn.fE;
    fSrcTransferFnCoeffs[5] = srcTransferFn.fF;
    fSrcTransferFnCoeffs[6] = srcTransferFn.fG;

    fDstTransferFnCoeffs[0] = dstTransferFn.fA;
    fDstTransferFnCoeffs[1] = dstTransferFn.fB;
    fDstTransferFnCoeffs[2] = dstTransferFn.fC;
    fDstTransferFnCoeffs[3] = dstTransferFn.fD;
    fDstTransferFnCoeffs[4] = dstTransferFn.fE;
    fDstTransferFnCoeffs[5] = dstTransferFn.fF;
    fDstTransferFnCoeffs[6] = dstTransferFn.fG;
}

GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect(
        const GrNonlinearColorSpaceXformEffect& that)
        : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
        , fGamutXform(that.fGamutXform)
        , fOps(that.fOps) {
    this->initClassID<GrNonlinearColorSpaceXformEffect>();
    memcpy(fSrcTransferFnCoeffs, that.fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs));
    memcpy(fDstTransferFnCoeffs, that.fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs));
}

sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::clone() const {
    return sk_sp<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(*this));
}

bool GrNonlinearColorSpaceXformEffect::onIsEqual(const GrFragmentProcessor& s) const {
    const GrNonlinearColorSpaceXformEffect& other = s.cast<GrNonlinearColorSpaceXformEffect>();
    if (other.fOps != fOps) {
        return false;
    }
    if (SkToBool(fOps & kSrcTransfer_Op) &&
        memcmp(&other.fSrcTransferFnCoeffs, &fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs))) {
        return false;
    }
    if (SkToBool(fOps & kDstTransfer_Op) &&
        memcmp(&other.fDstTransferFnCoeffs, &fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs))) {
        return false;
    }
    if (SkToBool(fOps & kGamutXform_Op) && other.fGamutXform != fGamutXform) {
        return false;
    }
    return true;
}

///////////////////////////////////////////////////////////////////////////////

GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrNonlinearColorSpaceXformEffect);

#if GR_TEST_UTILS
sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::TestCreate(GrProcessorTestData* d) {
    // TODO: Generate a random variety of color spaces for this effect (it can handle wacky
    // transfer functions, etc...)
    sk_sp<SkColorSpace> srcSpace = SkColorSpace::MakeSRGBLinear();
    sk_sp<SkColorSpace> dstSpace = SkColorSpace::MakeSRGB();
    return GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), dstSpace.get());
}
#endif

///////////////////////////////////////////////////////////////////////////////

void GrNonlinearColorSpaceXformEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
                                                             GrProcessorKeyBuilder* b) const {
    GrGLNonlinearColorSpaceXformEffect::GenKey(*this, caps, b);
}

GrGLSLFragmentProcessor* GrNonlinearColorSpaceXformEffect::onCreateGLSLInstance() const {
    return new GrGLNonlinearColorSpaceXformEffect();
}

sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::Make(const SkColorSpace* src,
                                                                  const SkColorSpace* dst) {
    if (!src || !dst || SkColorSpace::Equals(src, dst)) {
        // No conversion possible (or necessary)
        return nullptr;
    }

    uint32_t ops = 0;

    // We rely on GrColorSpaceXform to build the gamut xform matrix for us (to get caching)
    auto gamutXform = GrColorSpaceXform::Make(src, dst);
    SkMatrix44 srcToDstMtx(SkMatrix44::kUninitialized_Constructor);
    if (gamutXform) {
        ops |= kGamutXform_Op;
        srcToDstMtx = gamutXform->srcToDst();
    }

    SkColorSpaceTransferFn srcTransferFn;
    if (!src->gammaIsLinear()) {
        if (src->isNumericalTransferFn(&srcTransferFn)) {
            ops |= kSrcTransfer_Op;
        } else {
            return nullptr;
        }
    }

    SkColorSpaceTransferFn dstTransferFn;
    if (!dst->gammaIsLinear()) {
        if (dst->isNumericalTransferFn(&dstTransferFn)) {
            dstTransferFn = dstTransferFn.invert();
            ops |= kDstTransfer_Op;
        } else {
            return nullptr;
        }
    }

    return sk_sp<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(
            ops, srcTransferFn, dstTransferFn, srcToDstMtx));
}
<commit_msg>Don't clamp in GrGLNonlinearColorSpaceXformEffect to support scRGB<commit_after>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrNonlinearColorSpaceXformEffect.h"
#include "GrColorSpaceXform.h"
#include "GrProcessor.h"
#include "SkColorSpace_Base.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"

class GrGLNonlinearColorSpaceXformEffect : public GrGLSLFragmentProcessor {
public:
    void emitCode(EmitArgs& args) override {
        const GrNonlinearColorSpaceXformEffect& csxe =
                args.fFp.cast<GrNonlinearColorSpaceXformEffect>();
        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

        const char* srcCoeffsName = nullptr;
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
            fSrcTransferFnUni = uniformHandler->addUniformArray(
                    kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
                    "SrcTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                    &srcCoeffsName);
        }

        const char* dstCoeffsName = nullptr;
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
            fDstTransferFnUni = uniformHandler->addUniformArray(
                    kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
                    "DstTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                    &dstCoeffsName);
        }

        const char* gamutXformName = nullptr;
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
            fGamutXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType,
                                                        kDefault_GrSLPrecision, "GamutXform",
                                                        &gamutXformName);
        }

        // Helper function to apply the src or dst transfer function to a single value
        SkString tfFuncNames[2];
        for (size_t i = 0; i < 2; ++i) {
            const char* coeffsName = i ? dstCoeffsName : srcCoeffsName;
            if (!coeffsName) {
                continue;
            }
            const char* fnName = i ? "dst_transfer_fn" : "src_transfer_fn";
            static const GrShaderVar gTransferFnFuncArgs[] = {
                    GrShaderVar("x", kFloat_GrSLType),
            };
            SkString transferFnBody;
            // Temporaries to make evaluation line readable
            transferFnBody.printf("float A = %s[0];", coeffsName);
            transferFnBody.appendf("float B = %s[1];", coeffsName);
            transferFnBody.appendf("float C = %s[2];", coeffsName);
            transferFnBody.appendf("float D = %s[3];", coeffsName);
            transferFnBody.appendf("float E = %s[4];", coeffsName);
            transferFnBody.appendf("float F = %s[5];", coeffsName);
            transferFnBody.appendf("float G = %s[6];", coeffsName);
            transferFnBody.append("float s = sign(x);");
            transferFnBody.append("x = abs(x);");
            transferFnBody.appendf("return s * ((x < D) ? (C * x) + F : pow(A * x + B, G) + E);");
            fragBuilder->emitFunction(kFloat_GrSLType, fnName, SK_ARRAY_COUNT(gTransferFnFuncArgs),
                                      gTransferFnFuncArgs, transferFnBody.c_str(), &tfFuncNames[i]);
        }

        if (nullptr == args.fInputColor) {
            args.fInputColor = "vec4(1)";
        }
        fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);

        // 1: Un-premultiply the input color (if necessary)
        fragBuilder->codeAppendf("float nonZeroAlpha = max(color.a, 0.00001);");
        fragBuilder->codeAppendf("color = vec4(color.rgb / nonZeroAlpha, nonZeroAlpha);");

        // 2: Apply src transfer function (to get to linear RGB)
        if (srcCoeffsName) {
            fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[0].c_str());
            fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[0].c_str());
            fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[0].c_str());
        }

        // 3: Apply gamut matrix
        if (gamutXformName) {
            fragBuilder->codeAppendf(
                "color.rgb = (%s * vec4(color.rgb, 1.0)).rgb;", gamutXformName);
        }

        // 4: Apply dst transfer fn
        if (dstCoeffsName) {
            fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[1].c_str());
            fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[1].c_str());
            fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[1].c_str());
        }

        // 5: Premultiply again
        fragBuilder->codeAppendf("%s = vec4(color.rgb * color.a, color.a);", args.fOutputColor);
    }

    static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
                              GrProcessorKeyBuilder* b) {
        const GrNonlinearColorSpaceXformEffect& csxe =
                processor.cast<GrNonlinearColorSpaceXformEffect>();
        b->add32(csxe.ops());
    }

protected:
    void onSetData(const GrGLSLProgramDataManager& pdman,
                   const GrFragmentProcessor& processor) override {
        const GrNonlinearColorSpaceXformEffect& csxe =
                processor.cast<GrNonlinearColorSpaceXformEffect>();
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
            pdman.set1fv(fSrcTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                         csxe.srcTransferFnCoeffs());
        }
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
            pdman.set1fv(fDstTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
                         csxe.dstTransferFnCoeffs());
        }
        if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
            pdman.setSkMatrix44(fGamutXformUni, csxe.gamutXform());
        }
    }

private:
    GrGLSLProgramDataManager::UniformHandle fSrcTransferFnUni;
    GrGLSLProgramDataManager::UniformHandle fDstTransferFnUni;
    GrGLSLProgramDataManager::UniformHandle fGamutXformUni;

    typedef GrGLSLFragmentProcessor INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect(
    uint32_t ops, const SkColorSpaceTransferFn& srcTransferFn,
    const SkColorSpaceTransferFn& dstTransferFn, const SkMatrix44& gamutXform)
        : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
        , fGamutXform(gamutXform)
        , fOps(ops) {
    this->initClassID<GrNonlinearColorSpaceXformEffect>();

    fSrcTransferFnCoeffs[0] = srcTransferFn.fA;
    fSrcTransferFnCoeffs[1] = srcTransferFn.fB;
    fSrcTransferFnCoeffs[2] = srcTransferFn.fC;
    fSrcTransferFnCoeffs[3] = srcTransferFn.fD;
    fSrcTransferFnCoeffs[4] = srcTransferFn.fE;
    fSrcTransferFnCoeffs[5] = srcTransferFn.fF;
    fSrcTransferFnCoeffs[6] = srcTransferFn.fG;

    fDstTransferFnCoeffs[0] = dstTransferFn.fA;
    fDstTransferFnCoeffs[1] = dstTransferFn.fB;
    fDstTransferFnCoeffs[2] = dstTransferFn.fC;
    fDstTransferFnCoeffs[3] = dstTransferFn.fD;
    fDstTransferFnCoeffs[4] = dstTransferFn.fE;
    fDstTransferFnCoeffs[5] = dstTransferFn.fF;
    fDstTransferFnCoeffs[6] = dstTransferFn.fG;
}

GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect(
        const GrNonlinearColorSpaceXformEffect& that)
        : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
        , fGamutXform(that.fGamutXform)
        , fOps(that.fOps) {
    this->initClassID<GrNonlinearColorSpaceXformEffect>();
    memcpy(fSrcTransferFnCoeffs, that.fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs));
    memcpy(fDstTransferFnCoeffs, that.fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs));
}

sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::clone() const {
    return sk_sp<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(*this));
}

bool GrNonlinearColorSpaceXformEffect::onIsEqual(const GrFragmentProcessor& s) const {
    const GrNonlinearColorSpaceXformEffect& other = s.cast<GrNonlinearColorSpaceXformEffect>();
    if (other.fOps != fOps) {
        return false;
    }
    if (SkToBool(fOps & kSrcTransfer_Op) &&
        memcmp(&other.fSrcTransferFnCoeffs, &fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs))) {
        return false;
    }
    if (SkToBool(fOps & kDstTransfer_Op) &&
        memcmp(&other.fDstTransferFnCoeffs, &fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs))) {
        return false;
    }
    if (SkToBool(fOps & kGamutXform_Op) && other.fGamutXform != fGamutXform) {
        return false;
    }
    return true;
}

///////////////////////////////////////////////////////////////////////////////

GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrNonlinearColorSpaceXformEffect);

#if GR_TEST_UTILS
sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::TestCreate(GrProcessorTestData* d) {
    // TODO: Generate a random variety of color spaces for this effect (it can handle wacky
    // transfer functions, etc...)
    sk_sp<SkColorSpace> srcSpace = SkColorSpace::MakeSRGBLinear();
    sk_sp<SkColorSpace> dstSpace = SkColorSpace::MakeSRGB();
    return GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), dstSpace.get());
}
#endif

///////////////////////////////////////////////////////////////////////////////

void GrNonlinearColorSpaceXformEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
                                                             GrProcessorKeyBuilder* b) const {
    GrGLNonlinearColorSpaceXformEffect::GenKey(*this, caps, b);
}

GrGLSLFragmentProcessor* GrNonlinearColorSpaceXformEffect::onCreateGLSLInstance() const {
    return new GrGLNonlinearColorSpaceXformEffect();
}

sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::Make(const SkColorSpace* src,
                                                                  const SkColorSpace* dst) {
    if (!src || !dst || SkColorSpace::Equals(src, dst)) {
        // No conversion possible (or necessary)
        return nullptr;
    }

    uint32_t ops = 0;

    // We rely on GrColorSpaceXform to build the gamut xform matrix for us (to get caching)
    auto gamutXform = GrColorSpaceXform::Make(src, dst);
    SkMatrix44 srcToDstMtx(SkMatrix44::kUninitialized_Constructor);
    if (gamutXform) {
        ops |= kGamutXform_Op;
        srcToDstMtx = gamutXform->srcToDst();
    }

    SkColorSpaceTransferFn srcTransferFn;
    if (!src->gammaIsLinear()) {
        if (src->isNumericalTransferFn(&srcTransferFn)) {
            ops |= kSrcTransfer_Op;
        } else {
            return nullptr;
        }
    }

    SkColorSpaceTransferFn dstTransferFn;
    if (!dst->gammaIsLinear()) {
        if (dst->isNumericalTransferFn(&dstTransferFn)) {
            dstTransferFn = dstTransferFn.invert();
            ops |= kDstTransfer_Op;
        } else {
            return nullptr;
        }
    }

    return sk_sp<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(
            ops, srcTransferFn, dstTransferFn, srcToDstMtx));
}
<|endoftext|>


<commit_before>#include <mindbw/SQLite3.h>

#include <sqlite3.h>

#include <stdexcept>

#include <kizhi/Log.h>
#include <zephyr/to_string.h>

namespace mindbw
{

std::string operator_to_string(Operator op)
{
    switch (op) {
        case Operator::LT: return "<";
        case Operator::LTE: return "<=";
        case Operator::GT: return ">";
        case Operator::GTE: return ">=";
        case Operator::EQ: return "=";
        case Operator::NEQ: return "!=";
        case Operator::LET: return "=";
    }
}

std::string Esc(const std::string& s)
{
    auto query = sqlite3_mprintf("'%q'",s.c_str());
    auto ret = std::string(query);
    sqlite3_free(query);
    return ret;
}

std::string Compare(const std::string& key, const std::string& value,
        Operator op)
{
    return key + operator_to_string(op) + Esc(value);
}

std::string Equal(const std::string& key, const std::string& value)
{
    return Compare(key, value, Operator::EQ);
}

std::string Let(const std::string& key, const std::string& value)
{
    return Compare(key, value, Operator::LET);
}

std::string Like(const std::string& key, const std::string& value)
{
    return key + " LIKE " + Esc(std::string("%") + value + "%");
}

std::string Null()
{
    return "NULL";
}

std::string True()
{
    return "1";
}

std::string False()
{
    return "0";
}

std::string All()
{
    return "*";
}

std::string ValueList(const std::vector<std::string>& list)
{
    std::string s;
    if (list.empty()) {
        return s;
    }

    s += Null();
    for (auto&& l : list) {
        s += "," + Esc(l);
    }
    return s;
}

std::string KeyList(const std::vector<std::string>& list)
{
    std::string s;
    if (list.empty()) {
        return s;
    }

    s += Null();
    for (auto&& l : list) {
        s += "," + l;
    }
    return s;
}

std::string Unique(const std::string& s)
{
    return std::string("DISTINCT ") + s;
}

std::string And(const std::vector<std::string>& list)
{
    if (list.empty()) {
        return "1";
    }
    std::string where = "(";
    where += list[0];
    for (auto i = 1; i < list.size(); ++i) {
        where += " AND " + list[i];
    }
    where += ")";
    return where;
}

std::string Or(const std::vector<std::string>& list)
{
    if (list.empty()) {
        return "1";
    }
    std::string where = "(";
    where += list[0];
    for (auto i = 1; i < list.size(); ++i) {
        where += " OR " + list[i];
    }
    where += ")";
    return where;
}

SQLite3::SQLite3(const std::string& name) :
    SQLite3(name.c_str())
{
}

SQLite3::SQLite3(const char* name)
{
    if (sqlite3_open(name,&mDB)) {
        KIZHI_FATAL_T("SQLite3") << "Cannot open database: " << name
            << " : " << sqlite3_errmsg(mDB);
        throw std::runtime_error(std::string("Can't open database ") + name);
    }
    KIZHI_TRACE_T("SQLite3") << "Opening database " << mDB;
}

SQLite3::~SQLite3()
{
    KIZHI_TRACE_T("SQLite3") << "Closing database " << mDB;
    auto rc = sqlite3_close(mDB);
    if(rc == SQLITE_BUSY) {
        KIZHI_ERROR_T("SQLite3") << "Database closing failed! " << mDB;
        sqlite3_stmt* s;
        while((s = sqlite3_next_stmt(mDB, nullptr)) != nullptr) {
            sqlite3_finalize(s);
        }
        rc = sqlite3_close(mDB);
        if (rc != SQLITE_OK) {
            KIZHI_FATAL_T("SQLite3") << "Unable to finalize database! " << mDB;
        }
    }

}

void SQLite3::select(const std::string& select, const std::string& from,
            const std::string& where, const DataMapFn& f)
{
    std::string sql("SELECT ");
    sql += select + " FROM " + from + " WHERE " + where;
    exec(sql,f);
}

std::string SQLite3::insert(const std::string& insert, const std::string& values)
{
    std::string sql("INSERT INTO ");
    sql += insert + " VALUES(" + values + ")";
    exec(sql);
    return zephyr::to_string(sqlite3_last_insert_rowid(mDB));
}

void SQLite3::update(const std::string& update, const std::string& set,
        const std::string& where)
{
    std::string sql("UPDATE ");
    sql += update + " SET " + set + " WHERE " + where;
    exec(sql);
}

void SQLite3::del(const std::string& del, const std::string& where)
{
    std::string sql("DELETE FROM ");
    sql += del + " WHERE " + where;
    exec(sql);
}

void SQLite3::exec(const std::string& statement, const DataMapFn& f)
{
    KIZHI_TRACE_T("SQLite3") << statement;
    sqlite3_stmt* s;
    if (sqlite3_prepare_v2(mDB,statement.c_str(),-1,&s,nullptr) == SQLITE_OK) {
        auto cols = sqlite3_column_count(s);
        while (true) {
            auto result = sqlite3_step(s);

            if (result == SQLITE_ROW) {
                DataMap ret;
                for (auto i = 0; i < cols; ++i) {
                    auto* data =
                        reinterpret_cast<const char*>(sqlite3_column_text(s,i));
                    auto col = sqlite3_column_name(s, i);
                    auto d = data ? data : "NULL";
                    KIZHI_TRACE_T("SQLite3") << col << "->" << d;
                    ret.insert({col,d});
                }
                f(ret);
            } else {
                break;
            }
        }
        // cleanup
        if (s != nullptr) {
            sqlite3_finalize(s);
        }
    } else {
        throw std::runtime_error(sqlite3_errmsg(mDB));
    }
}

}
<commit_msg>Add more error messages<commit_after>#include <mindbw/SQLite3.h>

#include <sqlite3.h>

#include <stdexcept>

#include <kizhi/Log.h>
#include <zephyr/to_string.h>

namespace mindbw
{

std::string operator_to_string(Operator op)
{
    switch (op) {
        case Operator::LT: return "<";
        case Operator::LTE: return "<=";
        case Operator::GT: return ">";
        case Operator::GTE: return ">=";
        case Operator::EQ: return "=";
        case Operator::NEQ: return "!=";
        case Operator::LET: return "=";
    }
}

std::string Esc(const std::string& s)
{
    auto query = sqlite3_mprintf("'%q'",s.c_str());
    auto ret = std::string(query);
    sqlite3_free(query);
    return ret;
}

std::string Compare(const std::string& key, const std::string& value,
        Operator op)
{
    return key + operator_to_string(op) + Esc(value);
}

std::string Equal(const std::string& key, const std::string& value)
{
    return Compare(key, value, Operator::EQ);
}

std::string Let(const std::string& key, const std::string& value)
{
    return Compare(key, value, Operator::LET);
}

std::string Like(const std::string& key, const std::string& value)
{
    return key + " LIKE " + Esc(std::string("%") + value + "%");
}

std::string Null()
{
    return "NULL";
}

std::string True()
{
    return "1";
}

std::string False()
{
    return "0";
}

std::string All()
{
    return "*";
}

std::string ValueList(const std::vector<std::string>& list)
{
    std::string s;
    if (list.empty()) {
        return s;
    }

    s += Null();
    for (auto&& l : list) {
        s += "," + Esc(l);
    }
    return s;
}

std::string KeyList(const std::vector<std::string>& list)
{
    std::string s;
    if (list.empty()) {
        return s;
    }

    s += Null();
    for (auto&& l : list) {
        s += "," + l;
    }
    return s;
}

std::string Unique(const std::string& s)
{
    return std::string("DISTINCT ") + s;
}

std::string And(const std::vector<std::string>& list)
{
    if (list.empty()) {
        return "1";
    }
    std::string where = "(";
    where += list[0];
    for (auto i = 1; i < list.size(); ++i) {
        where += " AND " + list[i];
    }
    where += ")";
    return where;
}

std::string Or(const std::vector<std::string>& list)
{
    if (list.empty()) {
        return "1";
    }
    std::string where = "(";
    where += list[0];
    for (auto i = 1; i < list.size(); ++i) {
        where += " OR " + list[i];
    }
    where += ")";
    return where;
}

SQLite3::SQLite3(const std::string& name) :
    SQLite3(name.c_str())
{
}

SQLite3::SQLite3(const char* name)
{
    if (sqlite3_open(name,&mDB)) {
        KIZHI_FATAL_T("SQLite3") << "Cannot open database: " << name
            << " : " << sqlite3_errmsg(mDB);
        throw std::runtime_error(std::string("Can't open database ") + name);
    }
    KIZHI_TRACE_T("SQLite3") << "Opening database " << mDB;
}

SQLite3::~SQLite3()
{
    KIZHI_TRACE_T("SQLite3") << "Closing database " << mDB;
    auto rc = sqlite3_close(mDB);
    if(rc == SQLITE_BUSY) {
        KIZHI_ERROR_T("SQLite3") << "Database closing failed! " << mDB;
        sqlite3_stmt* s;
        while((s = sqlite3_next_stmt(mDB, nullptr)) != nullptr) {
            sqlite3_finalize(s);
        }
        rc = sqlite3_close(mDB);
        if (rc != SQLITE_OK) {
            KIZHI_FATAL_T("SQLite3") << "Unable to finalize database! " << mDB;
        }
    }

}

void SQLite3::select(const std::string& select, const std::string& from,
            const std::string& where, const DataMapFn& f)
{
    std::string sql("SELECT ");
    sql += select + " FROM " + from + " WHERE " + where;
    exec(sql,f);
}

std::string SQLite3::insert(const std::string& insert, const std::string& values)
{
    std::string sql("INSERT INTO ");
    sql += insert + " VALUES(" + values + ")";
    exec(sql);
    return zephyr::to_string(sqlite3_last_insert_rowid(mDB));
}

void SQLite3::update(const std::string& update, const std::string& set,
        const std::string& where)
{
    std::string sql("UPDATE ");
    sql += update + " SET " + set + " WHERE " + where;
    exec(sql);
}

void SQLite3::del(const std::string& del, const std::string& where)
{
    std::string sql("DELETE FROM ");
    sql += del + " WHERE " + where;
    exec(sql);
}

void SQLite3::exec(const std::string& statement, const DataMapFn& f)
{
    KIZHI_TRACE_T("SQLite3") << statement;
    sqlite3_stmt* s;
    if (sqlite3_prepare_v2(mDB,statement.c_str(),-1,&s,nullptr) == SQLITE_OK) {
        auto cols = sqlite3_column_count(s);
        while (true) {
            auto result = sqlite3_step(s);

            if (result == SQLITE_ROW) {
                DataMap ret;
                for (auto i = 0; i < cols; ++i) {
                    auto* data =
                        reinterpret_cast<const char*>(sqlite3_column_text(s,i));
                    auto col = sqlite3_column_name(s, i);
                    auto d = data ? data : "NULL";
                    KIZHI_TRACE_T("SQLite3") << col << "->" << d;
                    ret.insert({col,d});
                }
                f(ret);
            } else {
                break;
            }
        }
        // cleanup
        if (s != nullptr) {
            if (sqlite3_finalize(s) != SQLITE_OK) {
                KIZHI_ERROR_T("SQLite3") << "Finalize failed: "
                    << sqlite3_errmsg(mDB);
            }
        }
    } else {
        KIZHI_ERROR_T("SQLite3") << "Prepare failed: " << sqlite3_errmsg(mDB);
        throw std::runtime_error(std::string("prepare failed: ") +
                sqlite3_errmsg(mDB));
    }
}

}
<|endoftext|>


<commit_before>//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DebugAnnotator11.cpp: D3D11 helpers for adding trace annotations.
//

#include "libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h"

#include "common/debug.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"

namespace rx
{

DebugAnnotator11::DebugAnnotator11()
    : mInitialized(false),
      mD3d11Module(nullptr),
      mUserDefinedAnnotation(nullptr)
{
    // D3D11 devices can't be created during DllMain.
    // We defer device creation until the object is actually used.
}

DebugAnnotator11::~DebugAnnotator11()
{
    if (mInitialized)
    {
        SafeRelease(mUserDefinedAnnotation);

#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
        FreeLibrary(mD3d11Module);
#endif // !ANGLE_ENABLE_WINDOWS_STORE
    }
}

void DebugAnnotator11::beginEvent(const wchar_t *eventName)
{
    initializeDevice();

    mUserDefinedAnnotation->BeginEvent(eventName);
}

void DebugAnnotator11::endEvent()
{
    initializeDevice();

    mUserDefinedAnnotation->EndEvent();
}

void DebugAnnotator11::setMarker(const wchar_t *markerName)
{
    initializeDevice();

    mUserDefinedAnnotation->SetMarker(markerName);
}

bool DebugAnnotator11::getStatus()
{
    // ID3DUserDefinedAnnotation::GetStatus doesn't work with the Graphics Diagnostics tools in Visual Studio 2013.

#if defined(_DEBUG) && defined(ANGLE_ENABLE_WINDOWS_STORE)
    // In the Windows Store, we can use IDXGraphicsAnalysis. The call to GetDebugInterface1 only succeeds if the app is under capture.
    // This should only be called in DEBUG mode.
    // If an app links against DXGIGetDebugInterface1 in release mode then it will fail Windows Store ingestion checks.
    IDXGraphicsAnalysis *graphicsAnalysis;
    DXGIGetDebugInterface1(0, IID_PPV_ARGS(&graphicsAnalysis));
    bool underCapture = (graphicsAnalysis != nullptr);
    SafeRelease(graphicsAnalysis);
    return underCapture;
#endif // _DEBUG && !ANGLE_ENABLE_WINDOWS_STORE

    // Otherwise, we have to return true here.
    return true;
}

void DebugAnnotator11::initializeDevice()
{
    if (!mInitialized)
    {
#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
        mD3d11Module = LoadLibrary(TEXT("d3d11.dll"));
        ASSERT(mD3d11Module);

        PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(mD3d11Module, "D3D11CreateDevice");
        ASSERT(D3D11CreateDevice != nullptr);
#endif // !ANGLE_ENABLE_WINDOWS_STORE

        ID3D11Device *device = nullptr;
        ID3D11DeviceContext *context = nullptr;

        HRESULT hr = E_FAIL;

        // Create a D3D_DRIVER_TYPE_NULL device, which is much cheaper than other types of device.
        hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_NULL, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &context);
        ASSERT(SUCCEEDED(hr));
        if (SUCCEEDED(hr))
        {
            mUserDefinedAnnotation = d3d11::DynamicCastComObject<ID3DUserDefinedAnnotation>(context);
            ASSERT(mUserDefinedAnnotation != nullptr);
            mInitialized = true;
        }

        SafeRelease(device);
        SafeRelease(context);
    }
}

}
<commit_msg>In WinRT, prevent too many calls to DXGIGetDebugInterface1<commit_after>//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DebugAnnotator11.cpp: D3D11 helpers for adding trace annotations.
//

#include "libANGLE/renderer/d3d/d3d11/DebugAnnotator11.h"

#include "common/debug.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"

namespace rx
{

DebugAnnotator11::DebugAnnotator11()
    : mInitialized(false),
      mD3d11Module(nullptr),
      mUserDefinedAnnotation(nullptr)
{
    // D3D11 devices can't be created during DllMain.
    // We defer device creation until the object is actually used.
}

DebugAnnotator11::~DebugAnnotator11()
{
    if (mInitialized)
    {
        SafeRelease(mUserDefinedAnnotation);

#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
        FreeLibrary(mD3d11Module);
#endif // !ANGLE_ENABLE_WINDOWS_STORE
    }
}

void DebugAnnotator11::beginEvent(const wchar_t *eventName)
{
    initializeDevice();

    mUserDefinedAnnotation->BeginEvent(eventName);
}

void DebugAnnotator11::endEvent()
{
    initializeDevice();

    mUserDefinedAnnotation->EndEvent();
}

void DebugAnnotator11::setMarker(const wchar_t *markerName)
{
    initializeDevice();

    mUserDefinedAnnotation->SetMarker(markerName);
}

bool DebugAnnotator11::getStatus()
{
    // ID3DUserDefinedAnnotation::GetStatus doesn't work with the Graphics Diagnostics tools in Visual Studio 2013.
    static bool underCapture = true;

#if defined(_DEBUG) && defined(ANGLE_ENABLE_WINDOWS_STORE)
    // In the Windows Store, we can use IDXGraphicsAnalysis. The call to GetDebugInterface1 only succeeds if the app is under capture.
    // This should only be called in DEBUG mode.
    // If an app links against DXGIGetDebugInterface1 in release mode then it will fail Windows Store ingestion checks.

    // Cache the result to reduce the number of calls to DXGIGetDebugInterface1
    static bool triedIDXGraphicsAnalysis = false;

    if (!triedIDXGraphicsAnalysis)
    {
        IDXGraphicsAnalysis *graphicsAnalysis = nullptr;

        HRESULT result = DXGIGetDebugInterface1(0, IID_PPV_ARGS(&graphicsAnalysis));
        if (SUCCEEDED(result))
        {
            underCapture = (graphicsAnalysis != nullptr);
        }

        SafeRelease(graphicsAnalysis);
        triedIDXGraphicsAnalysis = true;
    }
#endif // _DEBUG && !ANGLE_ENABLE_WINDOWS_STORE

    return underCapture;
}

void DebugAnnotator11::initializeDevice()
{
    if (!mInitialized)
    {
#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
        mD3d11Module = LoadLibrary(TEXT("d3d11.dll"));
        ASSERT(mD3d11Module);

        PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(mD3d11Module, "D3D11CreateDevice");
        ASSERT(D3D11CreateDevice != nullptr);
#endif // !ANGLE_ENABLE_WINDOWS_STORE

        ID3D11Device *device = nullptr;
        ID3D11DeviceContext *context = nullptr;

        HRESULT hr = E_FAIL;

        // Create a D3D_DRIVER_TYPE_NULL device, which is much cheaper than other types of device.
        hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_NULL, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &device, nullptr, &context);
        ASSERT(SUCCEEDED(hr));
        if (SUCCEEDED(hr))
        {
            mUserDefinedAnnotation = d3d11::DynamicCastComObject<ID3DUserDefinedAnnotation>(context);
            ASSERT(mUserDefinedAnnotation != nullptr);
            mInitialized = true;
        }

        SafeRelease(device);
        SafeRelease(context);
    }
}

}
<|endoftext|>


<commit_before>#include "Texture.h"
#include <SDL_image.h>
#include <iostream>

Texture::Texture()
{
	m_pTexture = nullptr;
	m_pRenderer = nullptr;
	m_Width = 0;
	m_Height = 0;
}

Texture::~Texture()
{
	if (m_pTexture)
	{
		SDL_DestroyTexture(m_pTexture);
		m_pTexture = nullptr;
	}

	// The renderer will be deleted by the App class
	m_pRenderer = nullptr;

	m_Width = 0;
	m_Height = 0;
}

bool Texture::LoadFile(std::string filename, SDL_Renderer* renderer)
{
	// Check to make sure we're not already bound
	if (m_pTexture)
	{
		delete m_pTexture;
		m_pTexture = nullptr;
	}

	m_pTexture = IMG_LoadTexture(renderer, filename.c_str());
	if (!m_pTexture)
	{
		std::cout << "Error loading image '" << filename << "': " << IMG_GetError() << std::endl;
		return false;
	}

	// Get width and height
	SDL_QueryTexture(m_pTexture, NULL, NULL, &m_Width, &m_Height);

	m_pRenderer = renderer;
	return true;
}

void Texture::Render(int x, int y, SDL_Rect* clip)
{
	SDL_Rect rect;
	rect.x = x;
	rect.y = y;
	rect.w = m_Width;
	rect.h = m_Height;

	if (clip)
	{
		rect.w = clip->w;
		rect.h = clip->h;
	}

	SDL_RenderCopy(m_pRenderer, m_pTexture, clip, &rect);
}

void Texture::SetColor(Uint8 r, Uint8 g, Uint8 b)
{
	if (m_pTexture)
	{
		SDL_SetTextureColorMod(m_pTexture, r, g, b);
	}
}<commit_msg>Fixed Deleting  a texture if its bound<commit_after>#include "Texture.h"
#include <SDL_image.h>
#include <iostream>

Texture::Texture()
{
	m_pTexture = nullptr;
	m_pRenderer = nullptr;
	m_Width = 0;
	m_Height = 0;
}

Texture::~Texture()
{
	if (m_pTexture)
	{
		SDL_DestroyTexture(m_pTexture);
		m_pTexture = nullptr;
	}

	// The renderer will be deleted by the App class
	m_pRenderer = nullptr;

	m_Width = 0;
	m_Height = 0;
}

bool Texture::LoadFile(std::string filename, SDL_Renderer* renderer)
{
	// Check to make sure we're not already bound
	if (m_pTexture)
	{
        SDL_DestroyTexture(m_pTexture)
		m_pTexture = nullptr;
	}

	m_pTexture = IMG_LoadTexture(renderer, filename.c_str());
	if (!m_pTexture)
	{
		std::cout << "Error loading image '" << filename << "': " << IMG_GetError() << std::endl;
		return false;
	}

	// Get width and height
	SDL_QueryTexture(m_pTexture, NULL, NULL, &m_Width, &m_Height);

	m_pRenderer = renderer;
	return true;
}

void Texture::Render(int x, int y, SDL_Rect* clip)
{
	SDL_Rect rect;
	rect.x = x;
	rect.y = y;
	rect.w = m_Width;
	rect.h = m_Height;

	if (clip)
	{
		rect.w = clip->w;
		rect.h = clip->h;
	}

	SDL_RenderCopy(m_pRenderer, m_pTexture, clip, &rect);
}

void Texture::SetColor(Uint8 r, Uint8 g, Uint8 b)
{
	if (m_pTexture)
	{
		SDL_SetTextureColorMod(m_pTexture, r, g, b);
	}
}<|endoftext|>


<commit_before>#include "Tracing.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "runtime/HalideRuntime.h"

namespace Halide {
namespace Internal {

int tracing_level() {
    char *trace = getenv("HL_TRACE");
    return trace ? atoi(trace) : 0;
}

using std::vector;
using std::map;
using std::string;

class InjectTracing : public IRMutator {
public:
    const map<string, Function> &env;
    Function output;
    int global_level;
    InjectTracing(const map<string, Function> &e,
                  Function o) : env(e),
                                output(o),
                                global_level(tracing_level()) {}

private:
    using IRMutator::visit;

    void visit(const Call *op) {
        IRMutator::visit(op);
        op = expr.as<Call>();
        assert(op);

        if (op->call_type != Call::Halide) return;

        Function f = op->func;
        bool inlined = !f.same_as(output) && f.schedule().compute_level.is_inline();

        if (f.is_tracing_loads() || (global_level > 2 && !inlined)) {

            // Wrap the load in a call to trace_load
            vector<Expr> args;
            args.push_back(f.name());
            args.push_back(halide_trace_load);
            args.push_back(Variable::make(Int(32), op->name + ".trace_id"));
            args.push_back(op->value_index);
            args.push_back(op);
            args.insert(args.end(), op->args.begin(), op->args.end());

            expr = Call::make(op->type, Call::trace_expr, args, Call::Intrinsic);
        }
    }

    void visit(const Provide *op) {
        IRMutator::visit(op);
        op = stmt.as<Provide>();
        assert(op);

        map<string, Function>::const_iterator iter = env.find(op->name);
        if (iter == env.end()) return;
        Function f = iter->second;
        bool inlined = !f.same_as(output) && f.schedule().compute_level.is_inline();

        if (f.is_tracing_stores() || (global_level > 1 && !inlined)) {
            // Wrap each expr in a tracing call

            const vector<Expr> &values = op->values;
            vector<Expr> traces(op->values.size());

            for (size_t i = 0; i < values.size(); i++) {
                vector<Expr> args;
                args.push_back(f.name());
                args.push_back(halide_trace_store);
                args.push_back(Variable::make(Int(32), op->name + ".trace_id"));
                args.push_back((int)i);
                args.push_back(values[i]);
                args.insert(args.end(), op->args.begin(), op->args.end());
                traces[i] = Call::make(values[i].type(), Call::trace_expr, args, Call::Intrinsic);
            }

            stmt = Provide::make(op->name, traces, op->args);
        }
    }

    void visit(const Realize *op) {
        IRMutator::visit(op);
        op = stmt.as<Realize>();
        assert(op);

        map<string, Function>::const_iterator iter = env.find(op->name);
        if (iter == env.end()) return;
        Function f = iter->second;
        if (f.is_tracing_realizations() || global_level > 0) {

            // Throw a tracing call before and after the realize body
            vector<Expr> args;
            args.push_back(op->name);
            args.push_back(halide_trace_begin_realization); // event type for begin realization
            args.push_back(0); // realization id

            args.push_back(0); // value index
            args.push_back(0); // value

            for (size_t i = 0; i < op->bounds.size(); i++) {
                args.push_back(op->bounds[i].min);
                args.push_back(op->bounds[i].extent);
            }

            // Begin realization returns a unique token to pass to further trace calls affecting this buffer.

            Expr call_before = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            args[1] = halide_trace_end_realization;
            args[2] = Variable::make(Int(32), op->name + ".trace_id");
            Expr call_after = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            Stmt new_body = op->body;
            new_body = Block::make(new_body, Evaluate::make(call_after));
            new_body = LetStmt::make(op->name + ".trace_id", call_before, new_body);
            stmt = Realize::make(op->name, op->types, op->bounds, new_body);
        } else if (f.is_tracing_stores() || f.is_tracing_loads()) {
            // We need a trace id defined to pass to the loads and stores
            Stmt new_body = op->body;
            new_body = LetStmt::make(op->name + ".trace_id", 0, new_body);
            stmt = Realize::make(op->name, op->types, op->bounds, new_body);
        }

    }

    void visit(const Pipeline *op) {
        IRMutator::visit(op);
        op = stmt.as<Pipeline>();
        assert(op);
        map<string, Function>::const_iterator iter = env.find(op->name);
        if (iter == env.end()) return;
        Function f = iter->second;
        if (f.is_tracing_realizations() || global_level > 0) {
            // Throw a tracing call around each pipeline event
            vector<Expr> args;
            args.push_back(op->name);
            args.push_back(0);
            args.push_back(Variable::make(Int(32), op->name + ".trace_id"));
            args.push_back(0); // value index
            args.push_back(0); // value

            // Use the size of the pure step

            for (int i = 0; i < f.dimensions(); i++) {
                Expr min = Variable::make(Int(32), f.name() + ".s0." + f.args()[i] + ".min");
                Expr max = Variable::make(Int(32), f.name() + ".s0." + f.args()[i] + ".max");
                Expr extent = (max + 1) - min;
                args.push_back(min);
                args.push_back(extent);
            }

            Expr call;
            Stmt new_update;
            if (op->update.defined()) {
                args[1] = halide_trace_update;
                call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
                new_update = Block::make(Evaluate::make(call), op->update);
            }

            args[1] = halide_trace_consume;
            call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            Stmt new_consume = Block::make(Evaluate::make(call), op->consume);

            args[1] = halide_trace_end_consume;
            call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            new_consume = Block::make(new_consume, Evaluate::make(call));

            stmt = Pipeline::make(op->name, op->produce, new_update, new_consume);

            args[1] = halide_trace_produce;
            call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            stmt = LetStmt::make(f.name() + ".trace_id", call, stmt);
        }

    }
};

Stmt inject_tracing(Stmt s, const map<string, Function> &env, Function output) {
    Stmt original = s;
    InjectTracing tracing(env, output);

    // Add a dummy realize block for the output buffers
    Region output_region;
    Parameter output_buf = output.output_buffers()[0];
    assert(output_buf.is_buffer());
    for (int i = 0; i < output.dimensions(); i++) {
        string d = int_to_string(i);
        Expr min = Variable::make(Int(32), output_buf.name() + ".min." + d);
        Expr extent = Variable::make(Int(32), output_buf.name() + ".extent." + d);
        output_region.push_back(Range(min, extent));
    }
    s = Realize::make(output.name(), output.output_types(), output_region, s);

    // Inject tracing calls
    s = tracing.mutate(s);

    // Strip off the dummy realize block
    const Realize *r = s.as<Realize>();
    assert(r);
    s = r->body;

    // Unless tracing was a no-op, add a call to shut down the trace
    // (which flushes the output stream)
    if (!s.same_as(original)) {
        Expr flush = Call::make(Int(32), "halide_shutdown_trace", vector<Expr>(), Call::Extern);
        s = Block::make(s, AssertStmt::make(flush == 0, "Failed to flush trace", vector<Expr>()));
    }
    return s;
}

}
}
<commit_msg>Fix bug where trace level 3 would wrap a load inside of an address_of<commit_after>#include "Tracing.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "runtime/HalideRuntime.h"

namespace Halide {
namespace Internal {

int tracing_level() {
    char *trace = getenv("HL_TRACE");
    return trace ? atoi(trace) : 0;
}

using std::vector;
using std::map;
using std::string;

class InjectTracing : public IRMutator {
public:
    const map<string, Function> &env;
    Function output;
    int global_level;
    InjectTracing(const map<string, Function> &e,
                  Function o) : env(e),
                                output(o),
                                global_level(tracing_level()) {}

private:
    using IRMutator::visit;

    void visit(const Call *op) {

        // Calls inside of an address_of don't count, but we want to
        // visit the args of the inner call.
        if (op->call_type == Call::Intrinsic && op->name == Call::address_of) {
            assert(op->args.size() == 1);
            const Call *c = op->args[0].as<Call>();

            bool unchanged = true;
            vector<Expr> new_args(c->args.size());
            for (size_t i = 0; i < c->args.size(); i++) {
                new_args[i] = mutate(c->args[i]);
                unchanged = unchanged && (new_args[i].same_as(c->args[i]));
            }

            if (unchanged) {
                expr = op;
                return;
            } else {
                Expr inner = Call::make(c->type, c->name, new_args, c->call_type,
                                        c->func, c->value_index, c->image, c->param);
                expr = Call::make(Handle(), Call::address_of, vec(inner), Call::Intrinsic);
                return;
            }

        }

        IRMutator::visit(op);
        op = expr.as<Call>();
        assert(op);

        if (op->call_type != Call::Halide) {
            return;
        }

        Function f = op->func;
        bool inlined = !f.same_as(output) && f.schedule().compute_level.is_inline();

        if (f.is_tracing_loads() || (global_level > 2 && !inlined)) {

            // Wrap the load in a call to trace_load
            vector<Expr> args;
            args.push_back(f.name());
            args.push_back(halide_trace_load);
            args.push_back(Variable::make(Int(32), op->name + ".trace_id"));
            args.push_back(op->value_index);
            args.push_back(op);
            args.insert(args.end(), op->args.begin(), op->args.end());

            expr = Call::make(op->type, Call::trace_expr, args, Call::Intrinsic);
        }

    }

    void visit(const Provide *op) {
        IRMutator::visit(op);
        op = stmt.as<Provide>();
        assert(op);

        map<string, Function>::const_iterator iter = env.find(op->name);
        if (iter == env.end()) return;
        Function f = iter->second;
        bool inlined = !f.same_as(output) && f.schedule().compute_level.is_inline();

        if (f.is_tracing_stores() || (global_level > 1 && !inlined)) {
            // Wrap each expr in a tracing call

            const vector<Expr> &values = op->values;
            vector<Expr> traces(op->values.size());

            for (size_t i = 0; i < values.size(); i++) {
                vector<Expr> args;
                args.push_back(f.name());
                args.push_back(halide_trace_store);
                args.push_back(Variable::make(Int(32), op->name + ".trace_id"));
                args.push_back((int)i);
                args.push_back(values[i]);
                args.insert(args.end(), op->args.begin(), op->args.end());
                traces[i] = Call::make(values[i].type(), Call::trace_expr, args, Call::Intrinsic);
            }

            stmt = Provide::make(op->name, traces, op->args);
        }
    }

    void visit(const Realize *op) {
        IRMutator::visit(op);
        op = stmt.as<Realize>();
        assert(op);

        map<string, Function>::const_iterator iter = env.find(op->name);
        if (iter == env.end()) return;
        Function f = iter->second;
        if (f.is_tracing_realizations() || global_level > 0) {

            // Throw a tracing call before and after the realize body
            vector<Expr> args;
            args.push_back(op->name);
            args.push_back(halide_trace_begin_realization); // event type for begin realization
            args.push_back(0); // realization id

            args.push_back(0); // value index
            args.push_back(0); // value

            for (size_t i = 0; i < op->bounds.size(); i++) {
                args.push_back(op->bounds[i].min);
                args.push_back(op->bounds[i].extent);
            }

            // Begin realization returns a unique token to pass to further trace calls affecting this buffer.

            Expr call_before = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            args[1] = halide_trace_end_realization;
            args[2] = Variable::make(Int(32), op->name + ".trace_id");
            Expr call_after = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            Stmt new_body = op->body;
            new_body = Block::make(new_body, Evaluate::make(call_after));
            new_body = LetStmt::make(op->name + ".trace_id", call_before, new_body);
            stmt = Realize::make(op->name, op->types, op->bounds, new_body);
        } else if (f.is_tracing_stores() || f.is_tracing_loads()) {
            // We need a trace id defined to pass to the loads and stores
            Stmt new_body = op->body;
            new_body = LetStmt::make(op->name + ".trace_id", 0, new_body);
            stmt = Realize::make(op->name, op->types, op->bounds, new_body);
        }

    }

    void visit(const Pipeline *op) {
        IRMutator::visit(op);
        op = stmt.as<Pipeline>();
        assert(op);
        map<string, Function>::const_iterator iter = env.find(op->name);
        if (iter == env.end()) return;
        Function f = iter->second;
        if (f.is_tracing_realizations() || global_level > 0) {
            // Throw a tracing call around each pipeline event
            vector<Expr> args;
            args.push_back(op->name);
            args.push_back(0);
            args.push_back(Variable::make(Int(32), op->name + ".trace_id"));
            args.push_back(0); // value index
            args.push_back(0); // value

            // Use the size of the pure step

            for (int i = 0; i < f.dimensions(); i++) {
                Expr min = Variable::make(Int(32), f.name() + ".s0." + f.args()[i] + ".min");
                Expr max = Variable::make(Int(32), f.name() + ".s0." + f.args()[i] + ".max");
                Expr extent = (max + 1) - min;
                args.push_back(min);
                args.push_back(extent);
            }

            Expr call;
            Stmt new_update;
            if (op->update.defined()) {
                args[1] = halide_trace_update;
                call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
                new_update = Block::make(Evaluate::make(call), op->update);
            }

            args[1] = halide_trace_consume;
            call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            Stmt new_consume = Block::make(Evaluate::make(call), op->consume);

            args[1] = halide_trace_end_consume;
            call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            new_consume = Block::make(new_consume, Evaluate::make(call));

            stmt = Pipeline::make(op->name, op->produce, new_update, new_consume);

            args[1] = halide_trace_produce;
            call = Call::make(Int(32), Call::trace, args, Call::Intrinsic);
            stmt = LetStmt::make(f.name() + ".trace_id", call, stmt);
        }

    }
};

Stmt inject_tracing(Stmt s, const map<string, Function> &env, Function output) {
    Stmt original = s;
    InjectTracing tracing(env, output);

    // Add a dummy realize block for the output buffers
    Region output_region;
    Parameter output_buf = output.output_buffers()[0];
    assert(output_buf.is_buffer());
    for (int i = 0; i < output.dimensions(); i++) {
        string d = int_to_string(i);
        Expr min = Variable::make(Int(32), output_buf.name() + ".min." + d);
        Expr extent = Variable::make(Int(32), output_buf.name() + ".extent." + d);
        output_region.push_back(Range(min, extent));
    }
    s = Realize::make(output.name(), output.output_types(), output_region, s);

    // Inject tracing calls
    s = tracing.mutate(s);

    // Strip off the dummy realize block
    const Realize *r = s.as<Realize>();
    assert(r);
    s = r->body;

    // Unless tracing was a no-op, add a call to shut down the trace
    // (which flushes the output stream)
    if (!s.same_as(original)) {
        Expr flush = Call::make(Int(32), "halide_shutdown_trace", vector<Expr>(), Call::Extern);
        s = Block::make(s, AssertStmt::make(flush == 0, "Failed to flush trace", vector<Expr>()));
    }
    return s;
}

}
}
<|endoftext|>