<commit_before>/*
 * Copyright 2015 - 2019 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/bstring.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/types.hpp>
#include <ox/std/vector.hpp>

#include "desctypes.hpp"
#include "optype.hpp"
#include "types.hpp"

namespace ox {

namespace detail {

template<bool>
struct BoolWrapper {
};

template<typename T, typename = BoolWrapper<true>>
struct preloadable: false_type {};

template<typename T>
struct preloadable<T, BoolWrapper<T::Preloadable>> {
	static constexpr bool value = T::Preloadable;
};

}

template<typename T>
static constexpr int indirectionLevels(T) {
	return 0;
}

template<typename T>
static constexpr int indirectionLevels(T *t) {
	return 1 + indirectionLevels(*t);
}

class TypeDescWriter {

	private:
		struct NameCatcher {

			TypeName name;

			template<typename T = std::nullptr_t>
			constexpr void setTypeInfo(const char *n = T::TypeName, int = T::Fields) noexcept {
				this->name = n;
			}

			template<typename T>
			[[nodiscard]] constexpr ox::Error field(const char*, T*, std::size_t) noexcept {
				return OxError(0);
			}

			template<typename T>
			[[nodiscard]] constexpr ox::Error field(const char*, T) noexcept {
				return OxError(0);
			}

		};

		TypeStore *m_typeStoreOwnerRef = nullptr;
		TypeStore *m_typeStore = nullptr;
		DescriptorType *m_type = nullptr;

	public:
		explicit TypeDescWriter(TypeStore *typeStore = nullptr);

		~TypeDescWriter();

		template<typename T>
		[[nodiscard]] ox::Error field(const char *name, T *val, std::size_t valLen);

		template<typename T>
		[[nodiscard]] ox::Error field(const char *name, T val);

		template<typename T>
		[[nodiscard]] ox::Error field(const char *name, T *val);

		template<typename T = std::nullptr_t>
		void setTypeInfo(const char *name = T::TypeName, int fields = T::Fields);

		[[nodiscard]] DescriptorType *definition() noexcept {
			return m_type;
		}

		static constexpr auto opType() {
			return OpType::WriteDefinition;
		}

	private:
		DescriptorType *type(int8_t *val, bool *alreadyExisted);
		DescriptorType *type(int16_t *val, bool *alreadyExisted);
		DescriptorType *type(int32_t *val, bool *alreadyExisted);
		DescriptorType *type(int64_t *val, bool *alreadyExisted);

		DescriptorType *type(uint8_t *val, bool *alreadyExisted);
		DescriptorType *type(uint16_t *val, bool *alreadyExisted);
		DescriptorType *type(uint32_t *val, bool *alreadyExisted);
		DescriptorType *type(uint64_t *val, bool *alreadyExisted);

		DescriptorType *type(bool *val, bool *alreadyExisted);

		DescriptorType *type(const char *val, bool *alreadyExisted);

		DescriptorType *type(SerStr val, bool *alreadyExisted);

		template<std::size_t sz>
		DescriptorType *type(BString<sz> *val, bool *alreadyExisted);

		template<typename T>
		DescriptorType *type(T *val, bool *alreadyExisted);

		template<typename U>
		DescriptorType *type(UnionView<U> val, bool *alreadyExisted);

		DescriptorType *getType(TypeName tn, PrimitiveType t, int b, bool *alreadyExisted);
};

// array handler
template<typename T>
ox::Error TypeDescWriter::field(const char *name, T *val, std::size_t) {
	if (m_type) {
		constexpr typename ox::remove_pointer<decltype(val)>::type *p = nullptr;
		bool alreadyExisted = false;
		const auto t = type(p, &alreadyExisted);
		oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
		if (t == nullptr) {
			type(p, &alreadyExisted);
		}
		m_type->fieldList.emplace_back(t, name, indirectionLevels(val), alreadyExisted ? t->typeName : "", !alreadyExisted);
		return OxError(0);
	}
	return OxError(1);
}

template<typename T>
ox::Error TypeDescWriter::field(const char *name, T val) {
	if (m_type) {
		bool alreadyExisted = false;
		const auto t = type(val, &alreadyExisted);
		oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
		m_type->fieldList.emplace_back(t, name, 0, alreadyExisted ? t->typeName : "", !alreadyExisted);
		return OxError(0);
	}
	return OxError(1);
}

template<typename T>
ox::Error TypeDescWriter::field(const char *name, T *val) {
	if (m_type) {
		bool alreadyExisted = false;
		const auto t = type(val, &alreadyExisted);
		oxAssert(t != nullptr, "field(const char *name, T *val): Type not found or generated");
		m_type->fieldList.emplace_back(t, name, 0, alreadyExisted ? t->typeName : "", !alreadyExisted);
		return OxError(0);
	}
	return OxError(1);
}

template<std::size_t sz>
DescriptorType *TypeDescWriter::type(BString<sz> *val, bool *alreadyExisted) {
	return type(SerStr(val), alreadyExisted);
}

template<typename T>
DescriptorType *TypeDescWriter::type(T *val, bool *alreadyExisted) {
	NameCatcher nc;
	oxLogError(model(&nc, val));
	if (m_typeStore->contains(nc.name)) {
		*alreadyExisted = true;
		return m_typeStore->at(nc.name);
	} else {
		TypeDescWriter dw(m_typeStore);
		oxLogError(model(&dw, val));
		*alreadyExisted = false;
		return dw.m_type;
	}
}

template<typename U>
DescriptorType *TypeDescWriter::type(UnionView<U> val, bool *alreadyExisted) {
	return type(val.get(), alreadyExisted);
}

template<typename T>
void TypeDescWriter::setTypeInfo(const char *name, int) {
	auto &t = m_typeStore->at(name);
	if (!t) {
		t = new DescriptorType;
	}
	m_type = t;
	m_type->typeName = name;
	if (is_union_v<T>) {
		m_type->primitiveType = PrimitiveType::Union;
	} else {
		m_type->primitiveType = PrimitiveType::Struct;
	}
	m_type->preloadable = detail::preloadable<T>::value;
}

template<typename T>
[[nodiscard]] ValErr<DescriptorType*> buildTypeDef(T *val) {
	TypeDescWriter writer;
	Error err = model(&writer, val);
	return {writer.definition(), err};
}

template<typename T>
Error writeTypeDef(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) {
	auto def = buildTypeDef(val);
	auto err = def.error;
	if (!err) {
		oxReturnError(writeType(buff, buffLen, def.value, sizeOut));
	}
	delete def.value;
	return err;
}

}
<commit_msg>[ox/model] Add opType method to TypeDescWriter<commit_after>/*
 * Copyright 2015 - 2019 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include <ox/std/byteswap.hpp>
#include <ox/std/hashmap.hpp>
#include <ox/std/bstring.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/types.hpp>
#include <ox/std/vector.hpp>

#include "desctypes.hpp"
#include "optype.hpp"
#include "types.hpp"

namespace ox {

namespace detail {

template<bool>
struct BoolWrapper {
};

template<typename T, typename = BoolWrapper<true>>
struct preloadable: false_type {};

template<typename T>
struct preloadable<T, BoolWrapper<T::Preloadable>> {
	static constexpr bool value = T::Preloadable;
};

}

template<typename T>
static constexpr int indirectionLevels(T) {
	return 0;
}

template<typename T>
static constexpr int indirectionLevels(T *t) {
	return 1 + indirectionLevels(*t);
}

class TypeDescWriter {

	private:
		struct NameCatcher {

			TypeName name;

			template<typename T = std::nullptr_t>
			constexpr void setTypeInfo(const char *n = T::TypeName, int = T::Fields) noexcept {
				this->name = n;
			}

			template<typename T>
			[[nodiscard]] constexpr ox::Error field(const char*, T*, std::size_t) noexcept {
				return OxError(0);
			}

			template<typename T>
			[[nodiscard]] constexpr ox::Error field(const char*, T) noexcept {
				return OxError(0);
			}

			static constexpr auto opType() {
				return OpType::Write;
			}

		};

		TypeStore *m_typeStoreOwnerRef = nullptr;
		TypeStore *m_typeStore = nullptr;
		DescriptorType *m_type = nullptr;

	public:
		explicit TypeDescWriter(TypeStore *typeStore = nullptr);

		~TypeDescWriter();

		template<typename T>
		[[nodiscard]] ox::Error field(const char *name, T *val, std::size_t valLen);

		template<typename T>
		[[nodiscard]] ox::Error field(const char *name, T val);

		template<typename T>
		[[nodiscard]] ox::Error field(const char *name, T *val);

		template<typename T = std::nullptr_t>
		void setTypeInfo(const char *name = T::TypeName, int fields = T::Fields);

		[[nodiscard]] DescriptorType *definition() noexcept {
			return m_type;
		}

		static constexpr auto opType() {
			return OpType::WriteDefinition;
		}

	private:
		DescriptorType *type(int8_t *val, bool *alreadyExisted);
		DescriptorType *type(int16_t *val, bool *alreadyExisted);
		DescriptorType *type(int32_t *val, bool *alreadyExisted);
		DescriptorType *type(int64_t *val, bool *alreadyExisted);

		DescriptorType *type(uint8_t *val, bool *alreadyExisted);
		DescriptorType *type(uint16_t *val, bool *alreadyExisted);
		DescriptorType *type(uint32_t *val, bool *alreadyExisted);
		DescriptorType *type(uint64_t *val, bool *alreadyExisted);

		DescriptorType *type(bool *val, bool *alreadyExisted);

		DescriptorType *type(const char *val, bool *alreadyExisted);

		DescriptorType *type(SerStr val, bool *alreadyExisted);

		template<std::size_t sz>
		DescriptorType *type(BString<sz> *val, bool *alreadyExisted);

		template<typename T>
		DescriptorType *type(T *val, bool *alreadyExisted);

		template<typename U>
		DescriptorType *type(UnionView<U> val, bool *alreadyExisted);

		DescriptorType *getType(TypeName tn, PrimitiveType t, int b, bool *alreadyExisted);
};

// array handler
template<typename T>
ox::Error TypeDescWriter::field(const char *name, T *val, std::size_t) {
	if (m_type) {
		constexpr typename ox::remove_pointer<decltype(val)>::type *p = nullptr;
		bool alreadyExisted = false;
		const auto t = type(p, &alreadyExisted);
		oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
		if (t == nullptr) {
			type(p, &alreadyExisted);
		}
		m_type->fieldList.emplace_back(t, name, indirectionLevels(val), alreadyExisted ? t->typeName : "", !alreadyExisted);
		return OxError(0);
	}
	return OxError(1);
}

template<typename T>
ox::Error TypeDescWriter::field(const char *name, T val) {
	if (m_type) {
		bool alreadyExisted = false;
		const auto t = type(val, &alreadyExisted);
		oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
		m_type->fieldList.emplace_back(t, name, 0, alreadyExisted ? t->typeName : "", !alreadyExisted);
		return OxError(0);
	}
	return OxError(1);
}

template<typename T>
ox::Error TypeDescWriter::field(const char *name, T *val) {
	if (m_type) {
		bool alreadyExisted = false;
		const auto t = type(val, &alreadyExisted);
		oxAssert(t != nullptr, "field(const char *name, T *val): Type not found or generated");
		m_type->fieldList.emplace_back(t, name, 0, alreadyExisted ? t->typeName : "", !alreadyExisted);
		return OxError(0);
	}
	return OxError(1);
}

template<std::size_t sz>
DescriptorType *TypeDescWriter::type(BString<sz> *val, bool *alreadyExisted) {
	return type(SerStr(val), alreadyExisted);
}

template<typename T>
DescriptorType *TypeDescWriter::type(T *val, bool *alreadyExisted) {
	NameCatcher nc;
	oxLogError(model(&nc, val));
	if (m_typeStore->contains(nc.name)) {
		*alreadyExisted = true;
		return m_typeStore->at(nc.name);
	} else {
		TypeDescWriter dw(m_typeStore);
		oxLogError(model(&dw, val));
		*alreadyExisted = false;
		return dw.m_type;
	}
}

template<typename U>
DescriptorType *TypeDescWriter::type(UnionView<U> val, bool *alreadyExisted) {
	return type(val.get(), alreadyExisted);
}

template<typename T>
void TypeDescWriter::setTypeInfo(const char *name, int) {
	auto &t = m_typeStore->at(name);
	if (!t) {
		t = new DescriptorType;
	}
	m_type = t;
	m_type->typeName = name;
	if (is_union_v<T>) {
		m_type->primitiveType = PrimitiveType::Union;
	} else {
		m_type->primitiveType = PrimitiveType::Struct;
	}
	m_type->preloadable = detail::preloadable<T>::value;
}

template<typename T>
[[nodiscard]] ValErr<DescriptorType*> buildTypeDef(T *val) {
	TypeDescWriter writer;
	Error err = model(&writer, val);
	return {writer.definition(), err};
}

template<typename T>
Error writeTypeDef(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) {
	auto def = buildTypeDef(val);
	auto err = def.error;
	if (!err) {
		oxReturnError(writeType(buff, buffLen, def.value, sizeOut));
	}
	delete def.value;
	return err;
}

}
<|endoftext|>


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

// Author: jmarantz@google.com (Joshua Marantz)

#include "pagespeed/kernel/thread/queued_worker_pool.h"

#include <deque>
#include <set>
#include <vector>

#include "base/logging.h"
#include "pagespeed/kernel/base/abstract_mutex.h"
#include "pagespeed/kernel/base/condvar.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/thread_annotations.h"
#include "pagespeed/kernel/base/thread_system.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/base/waveform.h"
#include "pagespeed/kernel/thread/queued_worker.h"

namespace net_instaweb {

namespace {

inline void UpdateWaveform(Waveform* queue_size, int delta) {
  if ((queue_size != NULL) && (delta != 0)) {
    queue_size->AddDelta(delta);
  }
}

const size_t kUnboundedQueue = 0;

}  // namespace

QueuedWorkerPool::QueuedWorkerPool(
    int max_workers, StringPiece thread_name_base, ThreadSystem* thread_system)
    : thread_system_(thread_system),
      mutex_(thread_system_->NewMutex()),
      max_workers_(max_workers),
      shutdown_(false),
      queue_size_(NULL),
      load_shedding_threshold_(kNoLoadShedding) {
  thread_name_base.CopyToString(&thread_name_base_);
}

QueuedWorkerPool::~QueuedWorkerPool() {
  ShutDown();

  // Final shutdown (in case ShutDown was not called) and deletion of
  // sequences.
  for (int i = 0, n = all_sequences_.size(); i < n; ++i) {
    Sequence* sequence = all_sequences_[i];
    sequence->WaitForShutDown();
    delete sequence;
  }
}

void QueuedWorkerPool::ShutDown() {
  InitiateShutDown();
  WaitForShutDownComplete();
}

void QueuedWorkerPool::InitiateShutDown() {
  // Set the shutdown flag so that no one adds any more groups.
  {
    ScopedMutex lock(mutex_.get());
    if (shutdown_) {
      // ShutDown might be called explicitly and also from the destructor.
      // No workers should have magically re-appeared while in shutdown mode,
      // although the all_sequences_ vector may be non-empty since we don't
      // delete those till the pool itself is deleted.
      DCHECK(active_workers_.empty());
      DCHECK(available_workers_.empty());
      return;
    }
    shutdown_ = true;
  }

  // Clear out all the sequences, so that no one adds any more runnable
  // functions. We don't need to lock our access to all_sequences_ as
  // that can only be mutated when shutdown_ == false.
  for (int i = 0, n = all_sequences_.size(); i < n; ++i) {
    Sequence* sequence = all_sequences_[i];
    sequence->InitiateShutDown();
    // Do not delete the sequence; just leave it in shutdown-mode so no
    // further tasks will be started in the thread.
  }
}

void QueuedWorkerPool::WaitForShutDownComplete() {
  DCHECK(shutdown_);

  // The sequence shutdown was initiated in ::InitiateShutDown and now
  // we must wait for the sequences to all exit before we can delete
  // the worker object, otherwise segfaults will occur.
  for (int i = 0, n = all_sequences_.size(); i < n; ++i) {
    Sequence* sequence = all_sequences_[i];
    sequence->WaitForShutDown();
    // Do not delete the sequence; just leave it in shutdown-mode so no
    // further tasks will be started in the thread.
  }

  // Wait for all workers to complete whatever they were doing.
  //
  // TODO(jmarantz): attempt to cancel in-progress functions via
  // Function::set_quit_requested.  For now, we just complete the
  // currently running functions and then shut down.
  while (true) {
    QueuedWorker* worker = NULL;
    {
      ScopedMutex lock(mutex_.get());
      if (active_workers_.empty()) {
        break;
      }
      std::set<QueuedWorker*>::iterator p = active_workers_.begin();
      worker = *p;
      active_workers_.erase(p);
    }
    worker->ShutDown();
    delete worker;
  }

  // At this point there are no active tasks or workers, so we can stop
  // mutexing.
  for (int i = 0, n = available_workers_.size(); i < n; ++i) {
    QueuedWorker* worker = available_workers_[i];
    worker->ShutDown();
    delete worker;
  }
  available_workers_.clear();
}

// Runs computable tasks through a worker.  Note that a first
// candidate sequence is passed into this method, but we can start
// looking at a new sequence when the passed-in one is exhausted
void QueuedWorkerPool::Run(Sequence* sequence, QueuedWorker* worker) {
  while (sequence != NULL) {
    // This is a little unfair but we will continue to pull tasks from
    // the same sequence and run them until the sequence is exhausted.  This
    // avoids locking the pool's central mutex every time we want to
    // run a new task; we need only mutex at the sequence level.
    while (Function* function = sequence->NextFunction()) {
      function->CallRun();
    }

    // Once a sequence is exhausted see if there's another queued sequence,
    // If there are no available sequences, the worker gets put back into
    // the 'available' list to wait for another Sequence::Add.
    sequence = AssignWorkerToNextSequence(worker);
  }
}

QueuedWorkerPool::Sequence* QueuedWorkerPool::AssignWorkerToNextSequence(
    QueuedWorker* worker) {
  Sequence* sequence = NULL;
  ScopedMutex lock(mutex_.get());
  if (!shutdown_) {
    if (queued_sequences_.empty()) {
      int erased = active_workers_.erase(worker);
      DCHECK_EQ(1, erased);
      available_workers_.push_back(worker);
    } else {
      sequence = queued_sequences_.front();
      queued_sequences_.pop_front();
    }
  }
  return sequence;
}

void QueuedWorkerPool::QueueSequence(Sequence* sequence) {
  QueuedWorker* worker = NULL;
  Sequence* drop_sequence = NULL;
  {
    ScopedMutex lock(mutex_.get());
    if (available_workers_.empty()) {
      // If we have haven't yet initiated our full allotment of threads, add
      // on demand until we hit that limit.
      if (active_workers_.size() < max_workers_) {
        worker =
            new QueuedWorker(StrCat(thread_name_base_, "-",
                                    IntegerToString(active_workers_.size())),
                             thread_system_);
        worker->Start();
        active_workers_.insert(worker);
      } else {
        // No workers available: must queue the sequence.
        queued_sequences_.push_back(sequence);

        // If too many sequences are waiting, we will cancel the oldest
        // waiting one.
        if ((load_shedding_threshold_ != kNoLoadShedding) &&
            (queued_sequences_.size() >
             static_cast<size_t>(load_shedding_threshold_))) {
          drop_sequence = queued_sequences_.front();
          queued_sequences_.pop_front();
        }
      }
    } else {
      // We pulled a worker off the free-stack.
      worker = available_workers_.back();
      available_workers_.pop_back();
      active_workers_.insert(worker);
    }
  }

  if (drop_sequence != NULL) {
    drop_sequence->Cancel();
  }

  // Run the worker without holding the Pool lock.
  if (worker != NULL) {
    worker->RunInWorkThread(
        new MemberFunction2<QueuedWorkerPool, QueuedWorkerPool::Sequence*,
                            QueuedWorker*>(
            &QueuedWorkerPool::Run, this, sequence, worker));
  }
}

bool QueuedWorkerPool::AreBusy(const SequenceSet& sequences)
    NO_THREAD_SAFETY_ANALYSIS {
  // This is the only operation that accesses multiple workers at once.
  // We order our lock acquisitions by address comparisons to get
  // 2-phase locking, and thus avoid deadlock... With the ordering
  // done for us by SequenceSet already.

  for (SequenceSet::iterator i = sequences.begin(); i != sequences.end(); ++i) {
    (*i)->sequence_mutex_->Lock();
  }

  bool busy = false;
  for (SequenceSet::iterator i = sequences.begin(); i != sequences.end(); ++i) {
    if ((*i)->IsBusy()) {
      busy = true;
      break;
    }
  }

  for (SequenceSet::iterator i = sequences.begin(); i != sequences.end(); ++i) {
    (*i)->sequence_mutex_->Unlock();
  }

  return busy;
}

void QueuedWorkerPool::SetLoadSheddingThreshold(int x) {
  DCHECK((x > 0) || (x == kNoLoadShedding));
  load_shedding_threshold_ = x;
}

QueuedWorkerPool::Sequence* QueuedWorkerPool::NewSequence() {
  ScopedMutex lock(mutex_.get());
  Sequence* sequence = NULL;
  if (!shutdown_) {
    if (free_sequences_.empty()) {
      sequence = new Sequence(thread_system_, this);
      sequence->set_queue_size_stat(queue_size_);
      all_sequences_.push_back(sequence);
    } else {
      sequence = free_sequences_.back();
      free_sequences_.pop_back();
      sequence->Reset();
    }
  }
  return sequence;
}

void QueuedWorkerPool::FreeSequence(Sequence* sequence) {
  // If the sequence is inactive, then we can immediately
  // recycle it.  But if the sequence was busy, then we must
  // wait until it completes its last function to recycle it.
  // This will happen in QueuedWorkerPool::Sequence::NextFunction,
  // which will then call SequenceNoLongerActive.
  if (sequence->InitiateShutDown()) {
    ScopedMutex lock(mutex_.get());
    free_sequences_.push_back(sequence);
  }
}

void QueuedWorkerPool::SequenceNoLongerActive(Sequence* sequence) {
  ScopedMutex lock(mutex_.get());
  if (!shutdown_) {
    free_sequences_.push_back(sequence);
  }
}

QueuedWorkerPool::Sequence::Sequence(ThreadSystem* thread_system,
                                     QueuedWorkerPool* pool)
    : sequence_mutex_(thread_system->NewMutex()),
      pool_(pool),
      termination_condvar_(sequence_mutex_->NewCondvar()),
      queue_size_(NULL),
      max_queue_size_(kUnboundedQueue) {
  Reset();
}

void QueuedWorkerPool::Sequence::Reset() {
  ScopedMutex lock(sequence_mutex_.get());
  shutdown_ = false;
  active_ = false;
  DCHECK(work_queue_.empty());
}

QueuedWorkerPool::Sequence::~Sequence() {
  DCHECK(shutdown_);
  DCHECK(work_queue_.empty());
}

QueuedWorkerPool::Sequence::AddFunction::~AddFunction() {
}

bool QueuedWorkerPool::Sequence::InitiateShutDown() {
  ScopedMutex lock(sequence_mutex_.get());
  shutdown_ = true;
  return !active_;
}

void QueuedWorkerPool::Sequence::WaitForShutDown() {
  int num_canceled = 0;
  {
    ScopedMutex lock(sequence_mutex_.get());
    shutdown_ = true;
    pool_ = NULL;

    while (active_) {
      // We use a TimedWait rather than a Wait so that we don't deadlock if
      // active_ turns false after the above check and before the call to
      // TimedWait.
      termination_condvar_->TimedWait(Timer::kSecondMs);
    }
    num_canceled = CancelTasksOnWorkQueue();
    DCHECK(work_queue_.empty());
  }

  UpdateWaveform(queue_size_, -num_canceled);
}

int QueuedWorkerPool::Sequence::CancelTasksOnWorkQueue() {
  int num_canceled = 0;
  while (!work_queue_.empty()) {
    Function* function = work_queue_.front();
    work_queue_.pop_front();
    sequence_mutex_->Unlock();
    function->CallCancel();
    ++num_canceled;
    sequence_mutex_->Lock();
  }
  return num_canceled;
}

void QueuedWorkerPool::Sequence::Cancel() {
  int num_canceled = 0;
  {
    ScopedMutex lock(sequence_mutex_.get());
    num_canceled = CancelTasksOnWorkQueue();
  }

  UpdateWaveform(queue_size_, -num_canceled);
}

void QueuedWorkerPool::Sequence::Add(Function* function) {
  bool queue_sequence = false;
  bool cancel = false;
  {
    ScopedMutex lock(sequence_mutex_.get());
    if (shutdown_) {
      LOG(WARNING) << "Adding function to sequence " << this
                   << " after shutdown";
      cancel = true;
    } else {
      Function* function_to_add = function;
      if ((max_queue_size_ != kUnboundedQueue) &&
          (work_queue_.size() >= max_queue_size_)) {
        // Overflowing a bounded queue cancels the oldest function.  We
        // cancel old ones because those are likely to be lookups on behalf
        // of older HTML requests that are waiting to be retired.  We'd rather
        // retire them without optimization than delay them further with a
        // slow cache.
        function = work_queue_.front();
        work_queue_.pop_front();
        cancel = true;
      }

      work_queue_.push_back(function_to_add);
      queue_sequence = (!active_ && (work_queue_.size() == 1));
    }
  }
  if (cancel) {
    function->CallCancel();
  }
  if (queue_sequence) {
    pool_->QueueSequence(this);
  }
  UpdateWaveform(queue_size_, cancel ? 0 : 1);
}

void QueuedWorkerPool::Sequence::CancelPendingFunctions() {
  std::deque<Function*> cancel_queue;
  {
    ScopedMutex lock(sequence_mutex_.get());
    work_queue_.swap(cancel_queue);
  }
  UpdateWaveform(queue_size_, -static_cast<int>(cancel_queue.size()));
  while (!cancel_queue.empty()) {
    Function* f = cancel_queue.front();
    cancel_queue.pop_front();
    f->CallCancel();
  }
}

Function* QueuedWorkerPool::Sequence::NextFunction() {
  Function* function = NULL;
  QueuedWorkerPool* release_to_pool = NULL;
  int queue_size_delta = 0;
  {
    ScopedMutex lock(sequence_mutex_.get());
    if (shutdown_) {
      if (active_) {
        if (!work_queue_.empty()) {
          LOG(WARNING) << "Canceling " << work_queue_.size()
                       << " functions on sequence Shutdown";
          queue_size_delta -= CancelTasksOnWorkQueue();
        }
        active_ = false;

        // Note after the Signal(), the current sequence may be
        // deleted if we are in the process of shutting down the
        // entire pool, so no further access to member variables is
        // allowed.  Hence we copied the pool_ variable to a local
        // temp so we can return it.  Note also that if the pool is in
        // the process of shutting down, then pool_ will be NULL so we
        // won't bother to add the free_sequences_ list.  In any case
        // this will be cleaned on shutdown via all_sequences_.
        release_to_pool = pool_;
        termination_condvar_->Signal();
      }
    } else if (work_queue_.empty()) {
      active_ = false;
    } else {
      function = work_queue_.front();
      work_queue_.pop_front();
      active_ = true;
      --queue_size_delta;
    }
  }
  if (release_to_pool != NULL) {
    // If the entire pool is in the process of shutting down when
    // NextFunction is called, we don't need to add this to the
    // free list; the pool will directly delete all sequences from
    // QueuedWorkerPool::ShutDown().
    release_to_pool->SequenceNoLongerActive(this);
  }
  UpdateWaveform(queue_size_, queue_size_delta);

  return function;
}

bool QueuedWorkerPool::Sequence::IsBusy() {
  return active_ || !work_queue_.empty();
}

}  // namespace net_instaweb
<commit_msg>Suppress "Adding function to sequence after shutdown" message on release builds.<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Author: jmarantz@google.com (Joshua Marantz)

#include "pagespeed/kernel/thread/queued_worker_pool.h"

#include <deque>
#include <set>
#include <vector>

#include "base/logging.h"
#include "pagespeed/kernel/base/abstract_mutex.h"
#include "pagespeed/kernel/base/condvar.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/thread_annotations.h"
#include "pagespeed/kernel/base/thread_system.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/base/waveform.h"
#include "pagespeed/kernel/thread/queued_worker.h"

namespace net_instaweb {

namespace {

inline void UpdateWaveform(Waveform* queue_size, int delta) {
  if ((queue_size != NULL) && (delta != 0)) {
    queue_size->AddDelta(delta);
  }
}

const size_t kUnboundedQueue = 0;

}  // namespace

QueuedWorkerPool::QueuedWorkerPool(
    int max_workers, StringPiece thread_name_base, ThreadSystem* thread_system)
    : thread_system_(thread_system),
      mutex_(thread_system_->NewMutex()),
      max_workers_(max_workers),
      shutdown_(false),
      queue_size_(NULL),
      load_shedding_threshold_(kNoLoadShedding) {
  thread_name_base.CopyToString(&thread_name_base_);
}

QueuedWorkerPool::~QueuedWorkerPool() {
  ShutDown();

  // Final shutdown (in case ShutDown was not called) and deletion of
  // sequences.
  for (int i = 0, n = all_sequences_.size(); i < n; ++i) {
    Sequence* sequence = all_sequences_[i];
    sequence->WaitForShutDown();
    delete sequence;
  }
}

void QueuedWorkerPool::ShutDown() {
  InitiateShutDown();
  WaitForShutDownComplete();
}

void QueuedWorkerPool::InitiateShutDown() {
  // Set the shutdown flag so that no one adds any more groups.
  {
    ScopedMutex lock(mutex_.get());
    if (shutdown_) {
      // ShutDown might be called explicitly and also from the destructor.
      // No workers should have magically re-appeared while in shutdown mode,
      // although the all_sequences_ vector may be non-empty since we don't
      // delete those till the pool itself is deleted.
      DCHECK(active_workers_.empty());
      DCHECK(available_workers_.empty());
      return;
    }
    shutdown_ = true;
  }

  // Clear out all the sequences, so that no one adds any more runnable
  // functions. We don't need to lock our access to all_sequences_ as
  // that can only be mutated when shutdown_ == false.
  for (int i = 0, n = all_sequences_.size(); i < n; ++i) {
    Sequence* sequence = all_sequences_[i];
    sequence->InitiateShutDown();
    // Do not delete the sequence; just leave it in shutdown-mode so no
    // further tasks will be started in the thread.
  }
}

void QueuedWorkerPool::WaitForShutDownComplete() {
  DCHECK(shutdown_);

  // The sequence shutdown was initiated in ::InitiateShutDown and now
  // we must wait for the sequences to all exit before we can delete
  // the worker object, otherwise segfaults will occur.
  for (int i = 0, n = all_sequences_.size(); i < n; ++i) {
    Sequence* sequence = all_sequences_[i];
    sequence->WaitForShutDown();
    // Do not delete the sequence; just leave it in shutdown-mode so no
    // further tasks will be started in the thread.
  }

  // Wait for all workers to complete whatever they were doing.
  //
  // TODO(jmarantz): attempt to cancel in-progress functions via
  // Function::set_quit_requested.  For now, we just complete the
  // currently running functions and then shut down.
  while (true) {
    QueuedWorker* worker = NULL;
    {
      ScopedMutex lock(mutex_.get());
      if (active_workers_.empty()) {
        break;
      }
      std::set<QueuedWorker*>::iterator p = active_workers_.begin();
      worker = *p;
      active_workers_.erase(p);
    }
    worker->ShutDown();
    delete worker;
  }

  // At this point there are no active tasks or workers, so we can stop
  // mutexing.
  for (int i = 0, n = available_workers_.size(); i < n; ++i) {
    QueuedWorker* worker = available_workers_[i];
    worker->ShutDown();
    delete worker;
  }
  available_workers_.clear();
}

// Runs computable tasks through a worker.  Note that a first
// candidate sequence is passed into this method, but we can start
// looking at a new sequence when the passed-in one is exhausted
void QueuedWorkerPool::Run(Sequence* sequence, QueuedWorker* worker) {
  while (sequence != NULL) {
    // This is a little unfair but we will continue to pull tasks from
    // the same sequence and run them until the sequence is exhausted.  This
    // avoids locking the pool's central mutex every time we want to
    // run a new task; we need only mutex at the sequence level.
    while (Function* function = sequence->NextFunction()) {
      function->CallRun();
    }

    // Once a sequence is exhausted see if there's another queued sequence,
    // If there are no available sequences, the worker gets put back into
    // the 'available' list to wait for another Sequence::Add.
    sequence = AssignWorkerToNextSequence(worker);
  }
}

QueuedWorkerPool::Sequence* QueuedWorkerPool::AssignWorkerToNextSequence(
    QueuedWorker* worker) {
  Sequence* sequence = NULL;
  ScopedMutex lock(mutex_.get());
  if (!shutdown_) {
    if (queued_sequences_.empty()) {
      int erased = active_workers_.erase(worker);
      DCHECK_EQ(1, erased);
      available_workers_.push_back(worker);
    } else {
      sequence = queued_sequences_.front();
      queued_sequences_.pop_front();
    }
  }
  return sequence;
}

void QueuedWorkerPool::QueueSequence(Sequence* sequence) {
  QueuedWorker* worker = NULL;
  Sequence* drop_sequence = NULL;
  {
    ScopedMutex lock(mutex_.get());
    if (available_workers_.empty()) {
      // If we have haven't yet initiated our full allotment of threads, add
      // on demand until we hit that limit.
      if (active_workers_.size() < max_workers_) {
        worker =
            new QueuedWorker(StrCat(thread_name_base_, "-",
                                    IntegerToString(active_workers_.size())),
                             thread_system_);
        worker->Start();
        active_workers_.insert(worker);
      } else {
        // No workers available: must queue the sequence.
        queued_sequences_.push_back(sequence);

        // If too many sequences are waiting, we will cancel the oldest
        // waiting one.
        if ((load_shedding_threshold_ != kNoLoadShedding) &&
            (queued_sequences_.size() >
             static_cast<size_t>(load_shedding_threshold_))) {
          drop_sequence = queued_sequences_.front();
          queued_sequences_.pop_front();
        }
      }
    } else {
      // We pulled a worker off the free-stack.
      worker = available_workers_.back();
      available_workers_.pop_back();
      active_workers_.insert(worker);
    }
  }

  if (drop_sequence != NULL) {
    drop_sequence->Cancel();
  }

  // Run the worker without holding the Pool lock.
  if (worker != NULL) {
    worker->RunInWorkThread(
        new MemberFunction2<QueuedWorkerPool, QueuedWorkerPool::Sequence*,
                            QueuedWorker*>(
            &QueuedWorkerPool::Run, this, sequence, worker));
  }
}

bool QueuedWorkerPool::AreBusy(const SequenceSet& sequences)
    NO_THREAD_SAFETY_ANALYSIS {
  // This is the only operation that accesses multiple workers at once.
  // We order our lock acquisitions by address comparisons to get
  // 2-phase locking, and thus avoid deadlock... With the ordering
  // done for us by SequenceSet already.

  for (SequenceSet::iterator i = sequences.begin(); i != sequences.end(); ++i) {
    (*i)->sequence_mutex_->Lock();
  }

  bool busy = false;
  for (SequenceSet::iterator i = sequences.begin(); i != sequences.end(); ++i) {
    if ((*i)->IsBusy()) {
      busy = true;
      break;
    }
  }

  for (SequenceSet::iterator i = sequences.begin(); i != sequences.end(); ++i) {
    (*i)->sequence_mutex_->Unlock();
  }

  return busy;
}

void QueuedWorkerPool::SetLoadSheddingThreshold(int x) {
  DCHECK((x > 0) || (x == kNoLoadShedding));
  load_shedding_threshold_ = x;
}

QueuedWorkerPool::Sequence* QueuedWorkerPool::NewSequence() {
  ScopedMutex lock(mutex_.get());
  Sequence* sequence = NULL;
  if (!shutdown_) {
    if (free_sequences_.empty()) {
      sequence = new Sequence(thread_system_, this);
      sequence->set_queue_size_stat(queue_size_);
      all_sequences_.push_back(sequence);
    } else {
      sequence = free_sequences_.back();
      free_sequences_.pop_back();
      sequence->Reset();
    }
  }
  return sequence;
}

void QueuedWorkerPool::FreeSequence(Sequence* sequence) {
  // If the sequence is inactive, then we can immediately
  // recycle it.  But if the sequence was busy, then we must
  // wait until it completes its last function to recycle it.
  // This will happen in QueuedWorkerPool::Sequence::NextFunction,
  // which will then call SequenceNoLongerActive.
  if (sequence->InitiateShutDown()) {
    ScopedMutex lock(mutex_.get());
    free_sequences_.push_back(sequence);
  }
}

void QueuedWorkerPool::SequenceNoLongerActive(Sequence* sequence) {
  ScopedMutex lock(mutex_.get());
  if (!shutdown_) {
    free_sequences_.push_back(sequence);
  }
}

QueuedWorkerPool::Sequence::Sequence(ThreadSystem* thread_system,
                                     QueuedWorkerPool* pool)
    : sequence_mutex_(thread_system->NewMutex()),
      pool_(pool),
      termination_condvar_(sequence_mutex_->NewCondvar()),
      queue_size_(NULL),
      max_queue_size_(kUnboundedQueue) {
  Reset();
}

void QueuedWorkerPool::Sequence::Reset() {
  ScopedMutex lock(sequence_mutex_.get());
  shutdown_ = false;
  active_ = false;
  DCHECK(work_queue_.empty());
}

QueuedWorkerPool::Sequence::~Sequence() {
  DCHECK(shutdown_);
  DCHECK(work_queue_.empty());
}

QueuedWorkerPool::Sequence::AddFunction::~AddFunction() {
}

bool QueuedWorkerPool::Sequence::InitiateShutDown() {
  ScopedMutex lock(sequence_mutex_.get());
  shutdown_ = true;
  return !active_;
}

void QueuedWorkerPool::Sequence::WaitForShutDown() {
  int num_canceled = 0;
  {
    ScopedMutex lock(sequence_mutex_.get());
    shutdown_ = true;
    pool_ = NULL;

    while (active_) {
      // We use a TimedWait rather than a Wait so that we don't deadlock if
      // active_ turns false after the above check and before the call to
      // TimedWait.
      termination_condvar_->TimedWait(Timer::kSecondMs);
    }
    num_canceled = CancelTasksOnWorkQueue();
    DCHECK(work_queue_.empty());
  }

  UpdateWaveform(queue_size_, -num_canceled);
}

int QueuedWorkerPool::Sequence::CancelTasksOnWorkQueue() {
  int num_canceled = 0;
  while (!work_queue_.empty()) {
    Function* function = work_queue_.front();
    work_queue_.pop_front();
    sequence_mutex_->Unlock();
    function->CallCancel();
    ++num_canceled;
    sequence_mutex_->Lock();
  }
  return num_canceled;
}

void QueuedWorkerPool::Sequence::Cancel() {
  int num_canceled = 0;
  {
    ScopedMutex lock(sequence_mutex_.get());
    num_canceled = CancelTasksOnWorkQueue();
  }

  UpdateWaveform(queue_size_, -num_canceled);
}

void QueuedWorkerPool::Sequence::Add(Function* function) {
  bool queue_sequence = false;
  bool cancel = false;
  {
    ScopedMutex lock(sequence_mutex_.get());
    if (shutdown_) {
#ifndef NDEBUG
      LOG(WARNING) << "Adding function to sequence " << this
                   << " after shutdown";
#endif
      cancel = true;
    } else {
      Function* function_to_add = function;
      if ((max_queue_size_ != kUnboundedQueue) &&
          (work_queue_.size() >= max_queue_size_)) {
        // Overflowing a bounded queue cancels the oldest function.  We
        // cancel old ones because those are likely to be lookups on behalf
        // of older HTML requests that are waiting to be retired.  We'd rather
        // retire them without optimization than delay them further with a
        // slow cache.
        function = work_queue_.front();
        work_queue_.pop_front();
        cancel = true;
      }

      work_queue_.push_back(function_to_add);
      queue_sequence = (!active_ && (work_queue_.size() == 1));
    }
  }
  if (cancel) {
    function->CallCancel();
  }
  if (queue_sequence) {
    pool_->QueueSequence(this);
  }
  UpdateWaveform(queue_size_, cancel ? 0 : 1);
}

void QueuedWorkerPool::Sequence::CancelPendingFunctions() {
  std::deque<Function*> cancel_queue;
  {
    ScopedMutex lock(sequence_mutex_.get());
    work_queue_.swap(cancel_queue);
  }
  UpdateWaveform(queue_size_, -static_cast<int>(cancel_queue.size()));
  while (!cancel_queue.empty()) {
    Function* f = cancel_queue.front();
    cancel_queue.pop_front();
    f->CallCancel();
  }
}

Function* QueuedWorkerPool::Sequence::NextFunction() {
  Function* function = NULL;
  QueuedWorkerPool* release_to_pool = NULL;
  int queue_size_delta = 0;
  {
    ScopedMutex lock(sequence_mutex_.get());
    if (shutdown_) {
      if (active_) {
        if (!work_queue_.empty()) {
          LOG(WARNING) << "Canceling " << work_queue_.size()
                       << " functions on sequence Shutdown";
          queue_size_delta -= CancelTasksOnWorkQueue();
        }
        active_ = false;

        // Note after the Signal(), the current sequence may be
        // deleted if we are in the process of shutting down the
        // entire pool, so no further access to member variables is
        // allowed.  Hence we copied the pool_ variable to a local
        // temp so we can return it.  Note also that if the pool is in
        // the process of shutting down, then pool_ will be NULL so we
        // won't bother to add the free_sequences_ list.  In any case
        // this will be cleaned on shutdown via all_sequences_.
        release_to_pool = pool_;
        termination_condvar_->Signal();
      }
    } else if (work_queue_.empty()) {
      active_ = false;
    } else {
      function = work_queue_.front();
      work_queue_.pop_front();
      active_ = true;
      --queue_size_delta;
    }
  }
  if (release_to_pool != NULL) {
    // If the entire pool is in the process of shutting down when
    // NextFunction is called, we don't need to add this to the
    // free list; the pool will directly delete all sequences from
    // QueuedWorkerPool::ShutDown().
    release_to_pool->SequenceNoLongerActive(this);
  }
  UpdateWaveform(queue_size_, queue_size_delta);

  return function;
}

bool QueuedWorkerPool::Sequence::IsBusy() {
  return active_ || !work_queue_.empty();
}

}  // namespace net_instaweb
<|endoftext|>


<commit_before>#include <iostream>
#include <sstream>
#include <fstream>
#include <bitset>

#include <fastuidraw/util/util.hpp>
#include <fastuidraw/painter/painter.hpp>
#include <fastuidraw/painter/painter_header.hpp>

#include "sdl_painter_demo.hpp"

using namespace fastuidraw;

typedef std::bitset<32> bitset;

class painter_test:public sdl_painter_demo
{
public:
  painter_test(void):
    sdl_painter_demo("", true)
  {}

protected:

  void
  log_helper(const reference_counted_ptr<gl::Program> &pr,
             const std::string &prefix, GLenum shader_type)
  {
    unsigned int cnt;
    const char *label;

    cnt = pr->num_shaders(shader_type);
    if(cnt == 0)
      {
        return;
      }

    label = gl::Shader::gl_shader_type_label(shader_type);
    std::cout << label << "'s written to:\n";

    for(unsigned int i = 0; i < cnt; ++i)
      {
        std::ostringstream name, name_log;
        name << prefix << "."
             << gl::Shader::gl_shader_type_label(shader_type)
             << "." << i << ".glsl";
        name_log << prefix << "."
                 << gl::Shader::gl_shader_type_label(shader_type)
                 << "." << i << ".log";

        std::ofstream file(name.str().c_str());
        file << pr->shader_src_code(shader_type, i);
        std::cout << "\tSource #" << i << ":" << name.str() << "\n";

        std::ofstream file_log(name_log.str().c_str());
        file_log << pr->shader_compile_log(shader_type, i);
        std::cout << "\tCompile Log #" << i << ":" << name_log.str() << "\n";
      }
  }

  void
  log_program(const reference_counted_ptr<gl::Program> &pr,
              const std::string &prefix)
  {
    {
      std::ostringstream name;
      name << prefix << "program.log";

      std::ofstream file(name.str().c_str());
      file << pr->log();

      std::cout << "\n\nProgram Log and contents written to " << name.str() << "\n";
    }

    log_helper(pr, prefix, GL_VERTEX_SHADER);
    log_helper(pr, prefix, GL_FRAGMENT_SHADER);
    log_helper(pr, prefix, GL_GEOMETRY_SHADER);
    log_helper(pr, prefix, GL_TESS_EVALUATION_SHADER);
    log_helper(pr, prefix, GL_TESS_CONTROL_SHADER);

    if(pr->link_success())
      {
        std::cout << "Link success\n";
        pr->use_program();
      }
    else
      {
        std::cout << "Link Failed\n";
      }
  }

  void
  derived_init(int, int)
  {
    log_program(m_backend->program(gl::PainterBackendGL::program_all), "painter.all");
    log_program(m_backend->program(gl::PainterBackendGL::program_without_discard), "painter.without_discard");
    log_program(m_backend->program(gl::PainterBackendGL::program_with_discard), "painter.with_discard");

    std::cout << "\nUseful command to see shader after pre-processor:\n"
              << "\tsed 's/#version/@version/g' file.glsl | sed 's/#extension/@extension/g'"
              << " | cpp | grep -v \"#\" | sed '/^\\s*$/d'"
              << " | sed 's/@version/#version/g' | sed 's/@extension/#extension/g'\n";
  }

  void
  draw_frame(void)
  {
    end_demo(0);
  }

  void
  handle_event(const SDL_Event &ev)
  {
    switch(ev.type)
      {
      case SDL_QUIT:
        end_demo(0);
        break;

      case SDL_KEYUP:
        switch(ev.key.keysym.sym)
          {
          case SDLK_ESCAPE:
            end_demo(0);
            break;
          }
        break;
      };
  }

private:

};

int
main(int argc, char **argv)
{
  std::cout << std::setw(40) << "header_size = " << PainterHeader::header_size << "\n"
            << std::setw(40) << "clip_equations_data_size = " << PainterClipEquations::clip_data_size << "\n"
            << std::setw(40) << "item_matrix_data_size = " << PainterItemMatrix::matrix_data_size << "\n"
            << std::setw(40) << "image_data_size = " << PainterBrush::image_data_size << "\n"
            << std::setw(40) << "linear_gradient_data_size = " << PainterBrush::linear_gradient_data_size << "\n"
            << std::setw(40) << "radial_gradient_data_size = " << PainterBrush::radial_gradient_data_size << "\n"
            << std::setw(40) << "repeat_window_data_size = " << PainterBrush::repeat_window_data_size << "\n"
            << std::setw(40) << "transformation_matrix_data_size = " << PainterBrush::transformation_matrix_data_size << "\n"
            << std::setw(40) << "transformation_translation_data_size = " << PainterBrush::transformation_translation_data_size << "\n"
            << "\n"

            << std::setw(40) << "image_slack_bit0 = " << PainterBrush::image_slack_bit0 << "\n"
            << std::setw(40) << "image_slack_num_bits = " << PainterBrush::image_slack_num_bits << "\n"
            << std::setw(40) << "image_number_index_lookups_bit0 = " << PainterBrush::image_number_index_lookups_bit0 << "\n"
            << std::setw(40) << "image_number_index_lookups_num_bits = " << PainterBrush::image_number_index_lookups_num_bits << "\n"
            << "\n"

            << std::setw(40) << "image_mask = " << bitset(PainterBrush::image_mask) << "\n"
            << std::setw(40) << "gradient_mask = " << bitset(PainterBrush::gradient_mask) << "\n"
            << std::setw(40) << "radial_gradient_mask = " << bitset(PainterBrush::radial_gradient_mask) << "\n"
            << std::setw(40) << "gradient_repeat_mask = " << bitset(PainterBrush::gradient_repeat_mask) << "\n"
            << std::setw(40) << "repeat_window_mask = " << bitset(PainterBrush::repeat_window_mask) << "\n"
            << std::setw(40) << "transformation_translation_mask = " << bitset(PainterBrush::transformation_translation_mask) << "\n"
            << std::setw(40) << "transformation_matrix_mask = " << bitset(PainterBrush::transformation_matrix_mask) << "\n"

            << std::setw(40) << "stroked_number_offset_types = " << StrokedPath::point::number_offset_types << "\n"
            << std::setw(40) << "stroked_offset_type_bit0 = " << StrokedPath::point::offset_type_bit0 << "\n"
            << std::setw(40) << "stroked_offset_type_num_bits = " << StrokedPath::point::offset_type_num_bits << "\n"
            << std::setw(40) << "stroked_boundary_bit = " << StrokedPath::point::boundary_bit << "\n"
            << std::setw(40) << "stroked_depth_bit0 = " << StrokedPath::point::depth_bit0 << "\n"
            << std::setw(40) << "stroked_depth_num_bits = " << StrokedPath::point::depth_num_bits << "\n"
            << std::setw(40) << "stroked_join_bit = " << StrokedPath::point::join_bit << "\n"
            << std::setw(40) << "stroked_number_common_bits = " << StrokedPath::point::number_common_bits << "\n"
            << std::setw(40) << "stroked_normal0_y_sign_bit = " << StrokedPath::point::normal0_y_sign_bit << "\n"
            << std::setw(40) << "stroked_normal1_y_sign_bit = " << StrokedPath::point::normal1_y_sign_bit << "\n"
            << std::setw(40) << "stroked_sin_sign_bit = " << StrokedPath::point::sin_sign_bit << "\n"
            << std::setw(40) << "stroked_adjustable_cap_ending_bit = " << StrokedPath::point::adjustable_cap_ending_bit << "\n"
            << std::setw(40) << "stroked_bevel_edge_bit = " << StrokedPath::point::bevel_edge_bit << "\n";

  painter_test P;
  return P.main(argc, argv);

}
<commit_msg>demos/painter_test: correct name for program log file<commit_after>#include <iostream>
#include <sstream>
#include <fstream>
#include <bitset>

#include <fastuidraw/util/util.hpp>
#include <fastuidraw/painter/painter.hpp>
#include <fastuidraw/painter/painter_header.hpp>

#include "sdl_painter_demo.hpp"

using namespace fastuidraw;

typedef std::bitset<32> bitset;

class painter_test:public sdl_painter_demo
{
public:
  painter_test(void):
    sdl_painter_demo("", true)
  {}

protected:

  void
  log_helper(const reference_counted_ptr<gl::Program> &pr,
             const std::string &prefix, GLenum shader_type)
  {
    unsigned int cnt;
    const char *label;

    cnt = pr->num_shaders(shader_type);
    if(cnt == 0)
      {
        return;
      }

    label = gl::Shader::gl_shader_type_label(shader_type);
    std::cout << label << "'s written to:\n";

    for(unsigned int i = 0; i < cnt; ++i)
      {
        std::ostringstream name, name_log;
        name << prefix << "."
             << gl::Shader::gl_shader_type_label(shader_type)
             << "." << i << ".glsl";
        name_log << prefix << "."
                 << gl::Shader::gl_shader_type_label(shader_type)
                 << "." << i << ".log";

        std::ofstream file(name.str().c_str());
        file << pr->shader_src_code(shader_type, i);
        std::cout << "\tSource #" << i << ":" << name.str() << "\n";

        std::ofstream file_log(name_log.str().c_str());
        file_log << pr->shader_compile_log(shader_type, i);
        std::cout << "\tCompile Log #" << i << ":" << name_log.str() << "\n";
      }
  }

  void
  log_program(const reference_counted_ptr<gl::Program> &pr,
              const std::string &prefix)
  {
    {
      std::ostringstream name;
      name << prefix << ".program.log";

      std::ofstream file(name.str().c_str());
      file << pr->log();

      std::cout << "\n\nProgram Log and contents written to " << name.str() << "\n";
    }

    log_helper(pr, prefix, GL_VERTEX_SHADER);
    log_helper(pr, prefix, GL_FRAGMENT_SHADER);
    log_helper(pr, prefix, GL_GEOMETRY_SHADER);
    log_helper(pr, prefix, GL_TESS_EVALUATION_SHADER);
    log_helper(pr, prefix, GL_TESS_CONTROL_SHADER);

    if(pr->link_success())
      {
        std::cout << "Link success\n";
        pr->use_program();
      }
    else
      {
        std::cout << "Link Failed\n";
      }
  }

  void
  derived_init(int, int)
  {
    log_program(m_backend->program(gl::PainterBackendGL::program_all), "painter.all");
    log_program(m_backend->program(gl::PainterBackendGL::program_without_discard), "painter.without_discard");
    log_program(m_backend->program(gl::PainterBackendGL::program_with_discard), "painter.with_discard");

    std::cout << "\nUseful command to see shader after pre-processor:\n"
              << "\tsed 's/#version/@version/g' file.glsl | sed 's/#extension/@extension/g'"
              << " | cpp | grep -v \"#\" | sed '/^\\s*$/d'"
              << " | sed 's/@version/#version/g' | sed 's/@extension/#extension/g'\n";
  }

  void
  draw_frame(void)
  {
    end_demo(0);
  }

  void
  handle_event(const SDL_Event &ev)
  {
    switch(ev.type)
      {
      case SDL_QUIT:
        end_demo(0);
        break;

      case SDL_KEYUP:
        switch(ev.key.keysym.sym)
          {
          case SDLK_ESCAPE:
            end_demo(0);
            break;
          }
        break;
      };
  }

private:

};

int
main(int argc, char **argv)
{
  std::cout << std::setw(40) << "header_size = " << PainterHeader::header_size << "\n"
            << std::setw(40) << "clip_equations_data_size = " << PainterClipEquations::clip_data_size << "\n"
            << std::setw(40) << "item_matrix_data_size = " << PainterItemMatrix::matrix_data_size << "\n"
            << std::setw(40) << "image_data_size = " << PainterBrush::image_data_size << "\n"
            << std::setw(40) << "linear_gradient_data_size = " << PainterBrush::linear_gradient_data_size << "\n"
            << std::setw(40) << "radial_gradient_data_size = " << PainterBrush::radial_gradient_data_size << "\n"
            << std::setw(40) << "repeat_window_data_size = " << PainterBrush::repeat_window_data_size << "\n"
            << std::setw(40) << "transformation_matrix_data_size = " << PainterBrush::transformation_matrix_data_size << "\n"
            << std::setw(40) << "transformation_translation_data_size = " << PainterBrush::transformation_translation_data_size << "\n"
            << "\n"

            << std::setw(40) << "image_slack_bit0 = " << PainterBrush::image_slack_bit0 << "\n"
            << std::setw(40) << "image_slack_num_bits = " << PainterBrush::image_slack_num_bits << "\n"
            << std::setw(40) << "image_number_index_lookups_bit0 = " << PainterBrush::image_number_index_lookups_bit0 << "\n"
            << std::setw(40) << "image_number_index_lookups_num_bits = " << PainterBrush::image_number_index_lookups_num_bits << "\n"
            << "\n"

            << std::setw(40) << "image_mask = " << bitset(PainterBrush::image_mask) << "\n"
            << std::setw(40) << "gradient_mask = " << bitset(PainterBrush::gradient_mask) << "\n"
            << std::setw(40) << "radial_gradient_mask = " << bitset(PainterBrush::radial_gradient_mask) << "\n"
            << std::setw(40) << "gradient_repeat_mask = " << bitset(PainterBrush::gradient_repeat_mask) << "\n"
            << std::setw(40) << "repeat_window_mask = " << bitset(PainterBrush::repeat_window_mask) << "\n"
            << std::setw(40) << "transformation_translation_mask = " << bitset(PainterBrush::transformation_translation_mask) << "\n"
            << std::setw(40) << "transformation_matrix_mask = " << bitset(PainterBrush::transformation_matrix_mask) << "\n"

            << std::setw(40) << "stroked_number_offset_types = " << StrokedPath::point::number_offset_types << "\n"
            << std::setw(40) << "stroked_offset_type_bit0 = " << StrokedPath::point::offset_type_bit0 << "\n"
            << std::setw(40) << "stroked_offset_type_num_bits = " << StrokedPath::point::offset_type_num_bits << "\n"
            << std::setw(40) << "stroked_boundary_bit = " << StrokedPath::point::boundary_bit << "\n"
            << std::setw(40) << "stroked_depth_bit0 = " << StrokedPath::point::depth_bit0 << "\n"
            << std::setw(40) << "stroked_depth_num_bits = " << StrokedPath::point::depth_num_bits << "\n"
            << std::setw(40) << "stroked_join_bit = " << StrokedPath::point::join_bit << "\n"
            << std::setw(40) << "stroked_number_common_bits = " << StrokedPath::point::number_common_bits << "\n"
            << std::setw(40) << "stroked_normal0_y_sign_bit = " << StrokedPath::point::normal0_y_sign_bit << "\n"
            << std::setw(40) << "stroked_normal1_y_sign_bit = " << StrokedPath::point::normal1_y_sign_bit << "\n"
            << std::setw(40) << "stroked_sin_sign_bit = " << StrokedPath::point::sin_sign_bit << "\n"
            << std::setw(40) << "stroked_adjustable_cap_ending_bit = " << StrokedPath::point::adjustable_cap_ending_bit << "\n"
            << std::setw(40) << "stroked_bevel_edge_bit = " << StrokedPath::point::bevel_edge_bit << "\n";

  painter_test P;
  return P.main(argc, argv);

}
<|endoftext|>


<commit_before>// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "TestFixtures/Decoration.hpp"

class AutoFilterCollapseRulesTest:
  public testing::Test
{
public:
  AutoFilterCollapseRulesTest(void) {
    // All decorator tests must run from an initiated context
    AutoCurrentContext()->Initiate();
  }
};

class AcceptsConstReference {
public:
  AcceptsConstReference(void) :
    m_called(0)
  {}

  int m_called;

  void AutoFilter(const int& dataIn) {
    ++m_called;
  }
};

class AcceptsSharedPointer {
public:
  AcceptsSharedPointer(void) :
    m_called(0)
  {}

  int m_called;

  void AutoFilter(std::shared_ptr<const int> dataIn) {
    ++m_called;
  }
};

TEST_F(AutoFilterCollapseRulesTest, SharedPtrCollapse) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<AcceptsConstReference> constr_filter;
  AutoRequired<AcceptsSharedPointer> shared_filter;

  int constr_int = 222;
  std::shared_ptr<int> shared_int = std::make_shared<int>(232);
  ASSERT_TRUE(static_cast<bool>(shared_int));

  // Decorate(type X) calls AutoFilter(const type& X)
  // Decorate(type X) calls AutoFilter(shared_ptr<type> X)
  {
    auto packet = factory->NewPacket();
    packet->Decorate(constr_int);
    ASSERT_EQ(1, constr_filter->m_called) << "Called const reference method " << constr_filter->m_called << " times";
    ASSERT_EQ(1, shared_filter->m_called) << "Called shared pointer method " << shared_filter->m_called << " times";
  }
  constr_filter->m_called = 0;
  shared_filter->m_called = 0;

  // Decorate(shared_ptr<type> X) calls AutoFilter(const type& X)
  // Decorate(shared_ptr<type> X) calls AutoFilter(shared_ptr<type> X)
  // NOTE: Decorate(shared_ptr<T> X) shares ownership of X instead of copying.
  {
    auto packet = factory->NewPacket();
    packet->Decorate(shared_int);
    ASSERT_EQ(1, constr_filter->m_called) << "Called const reference method " << constr_filter->m_called << " times";
    ASSERT_EQ(1, shared_filter->m_called) << "Called shared pointer method " << shared_filter->m_called << " times";
    ASSERT_FALSE(shared_int.unique()) << "Argument of Decorate should be shared, not copied, when possible";
  }
  constr_filter->m_called = 0;
  shared_filter->m_called = 0;

  // DecorateImmediate(type X) calls AutoFilter(const type& X)
  // DecorateImmediate(type X) DOES NOT CALL AutoFilter(shared_ptr<type> X)
  // NOTE: This case is invalid, since DecorateImmediate assumes no validity of X after the function call,
  // so the construction of a shared_ptr from &X would violate the contract of shared_ptr type.
  // If an AutoFilter method assumed the validity of shared_ptr<type> Y, a copy could be made that might
  // become invalid.
  // NOTE: DecorateImmediate(shared_ptr<type> X) is unnecessary (a static_assert will be called).
  // Decorate(shared_ptr<type> X) will share the reference to X instead of making a copy.
  {
    auto packet = factory->NewPacket();
    packet->DecorateImmediate(constr_int);
    ASSERT_EQ(1, constr_filter->m_called) << "Called const reference method " << constr_filter->m_called << " times";
    ASSERT_EQ(1, shared_filter->m_called) << "Called shared pointer method " << shared_filter->m_called << " times";
  }
  constr_filter->m_called = 0;
  shared_filter->m_called = 0;
}

TEST_F(AutoFilterCollapseRulesTest, ConstCollapse) {
  AutoRequired<AutoPacketFactory> factory;
  AutoRequired<AcceptsConstReference> filter;

  // Test const shared_ptr
  auto packet1 = factory->NewPacket();

  std::shared_ptr<const int> dec1 = std::make_shared<const int>(42);
  packet1->Decorate(dec1);

  ASSERT_EQ(1, filter->m_called) << "'const T' decoration didn't resolve to 'T'";

  // Test const value
  auto packet2 = factory->NewPacket();

  const int dec2 = 42;
  packet2->Decorate(dec2);

  ASSERT_EQ(2, filter->m_called) << "'const T' decoration didn't resolve to 'T'";
}

TEST_F(AutoFilterCollapseRulesTest, SharedPointerAliasingRules) {
  AutoRequired<AutoPacketFactory> factory;
  bool gen1Called = false;
  bool gen2Called = false;

  *factory += [&gen1Called](AutoPacket&, std::shared_ptr<const int>) {
    gen1Called = true;
  };
  *factory += [&gen2Called](AutoPacket&, int) {
    gen2Called = true;
  };

  auto packet = factory->NewPacket();
  packet->Decorate<int>(55);

  ASSERT_TRUE(gen1Called) << "AutoFilter accepting a shared pointer was not called as expected";
  ASSERT_TRUE(gen2Called) << "AutoFilter accepting a decorated type was not called as expected";
}

class ProducesSharedPointer {
public:
  ProducesSharedPointer(void) :
    m_called(0)
  {}

  int m_called;

  void AutoFilter(std::shared_ptr<int>& output) {
    ++m_called;
    output = std::make_shared<int>(55);
  }
};

TEST_F(AutoFilterCollapseRulesTest, AutoFilterSharedAliasingRules) {
  AutoRequired<ProducesSharedPointer> produces;
  AutoRequired<FilterGen<int>> consumes;
  AutoRequired<AutoPacketFactory> factory;

  // Decorate the packet, verify attribute presence:
  auto packet = factory->NewPacket();
  ASSERT_TRUE(packet->Has<int>()) << "Filter producing a shared pointer of type int did not correctly collapse to the basic int type";
  ASSERT_EQ(55, autowiring::get<0>(consumes->m_args)) << "Filter consuming a shared pointer output was not called as expected";
}

TEST_F(AutoFilterCollapseRulesTest, UnsatisfiableDecoration) {
  AutoRequired<AutoPacketFactory> factory;

  bool bInvoked = false;
  *factory += [&](std::shared_ptr<const Decoration<0>> dec) {
    ASSERT_FALSE(dec) << "Decoration was attached to the packet when it should not have been";
    bInvoked = true;
  };
  *factory += [](const Decoration<0>&) {
    FAIL() << "A filter was invoked when it should have been unsatisfiable";
  };

  // Kick off what should be a launch of the shared pointer-accepting input
  auto packet = factory->NewPacket();
  packet->Decorate(std::shared_ptr<Decoration<0>>());

  // Ensure the correct decoration was invoked
  ASSERT_TRUE(bInvoked) << "AutoFilter was not invoked in an unsatisfiable case as expected";
}

TEST_F(AutoFilterCollapseRulesTest, SharedOutSelfAutoPrev) {
  AutoRequired<AutoPacketFactory> factory;
  AutoCurrentContext()->Initiate();

  auto hello = std::make_shared<std::string>("Hello world!");
  size_t nMsgs = 0;
  *factory += [&](auto_prev<std::string> lastMsg, std::shared_ptr<std::string>& msg) {
    msg = hello;
    if (lastMsg)
      nMsgs++;
  };

  auto packet1 = factory->NewPacket();
  auto packet2 = factory->NewPacket();

  ASSERT_TRUE(packet1->Has<std::string>());
  ASSERT_TRUE(packet2->Has<std::string>());
  ASSERT_EQ(1UL, nMsgs) << "auto_prev was not correctly set when a filter produces a shared_ptr output";
}
<commit_msg>Minor test refactors<commit_after>// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "TestFixtures/Decoration.hpp"

class AutoFilterCollapseRulesTest:
  public testing::Test
{
public:
  AutoFilterCollapseRulesTest(void) {
    // All decorator tests must run from an initiated context
    AutoCurrentContext()->Initiate();
  }
};

TEST_F(AutoFilterCollapseRulesTest, SharedPtrCollapse) {
  AutoRequired<AutoPacketFactory> factory;

  int contRefCallCount = 0;
  *factory += [&](const int& dataIn) {
    contRefCallCount++;
  };

  int sharedPtrRefCallCount = 0;
  std::shared_ptr<const int> dataInVal;
  *factory += [&](std::shared_ptr<const int> dataIn) {
    sharedPtrRefCallCount++;
    dataInVal = dataIn;
  };

  int constr_int = 222;
  std::shared_ptr<int> shared_int = std::make_shared<int>(232);
  ASSERT_TRUE(static_cast<bool>(shared_int));

  // Decorate(type X) calls AutoFilter(const type& X)
  // Decorate(type X) calls AutoFilter(shared_ptr<type> X)
  {
    auto packet = factory->NewPacket();
    packet->Decorate(constr_int);
    ASSERT_EQ(1, contRefCallCount) << "Called const reference method " << contRefCallCount << " times";
    ASSERT_EQ(1, sharedPtrRefCallCount) << "Called shared pointer method " << sharedPtrRefCallCount << " times";
  }
  contRefCallCount = 0;
  sharedPtrRefCallCount = 0;

  // Decorate(shared_ptr<type> X) calls AutoFilter(const type& X)
  // Decorate(shared_ptr<type> X) calls AutoFilter(shared_ptr<type> X)
  // NOTE: Decorate(shared_ptr<T> X) shares ownership of X instead of copying.
  {
    auto packet = factory->NewPacket();
    packet->Decorate(shared_int);
    ASSERT_EQ(1, contRefCallCount) << "Called const reference method " << contRefCallCount << " times";
    ASSERT_EQ(1, sharedPtrRefCallCount) << "Called shared pointer method " << sharedPtrRefCallCount << " times";
    ASSERT_FALSE(shared_int.unique()) << "Argument of Decorate should be shared, not copied, when possible";
  }
  contRefCallCount = 0;
  sharedPtrRefCallCount = 0;

  // DecorateImmediate(type X) calls AutoFilter(const type& X)
  // DecorateImmediate(type X) DOES NOT CALL AutoFilter(shared_ptr<type> X)
  // NOTE: This case is invalid, since DecorateImmediate assumes no validity of X after the function call,
  // so the construction of a shared_ptr from &X would violate the contract of shared_ptr type.
  // If an AutoFilter method assumed the validity of shared_ptr<type> Y, a copy could be made that might
  // become invalid.
  // NOTE: DecorateImmediate(shared_ptr<type> X) is unnecessary (a static_assert will be called).
  // Decorate(shared_ptr<type> X) will share the reference to X instead of making a copy.
  {
    auto packet = factory->NewPacket();
    packet->DecorateImmediate(constr_int);
    ASSERT_EQ(1, contRefCallCount) << "Called const reference method " << contRefCallCount << " times";
    ASSERT_EQ(1, sharedPtrRefCallCount) << "Shared pointer method not called as expected";
    ASSERT_EQ(nullptr, dataInVal) << "A non-null shared pointer was received by an AutoFilter method during a DecorateImmediate call";
  }
  contRefCallCount = 0;
  sharedPtrRefCallCount = 0;
}

TEST_F(AutoFilterCollapseRulesTest, ConstCollapse) {
  AutoRequired<AutoPacketFactory> factory;

  int callCount = 0;
  *factory += [&](const int& dataIn) {
    callCount++;
  };

  // Test const shared_ptr
  auto packet1 = factory->NewPacket();

  std::shared_ptr<const int> dec1 = std::make_shared<const int>(42);
  packet1->Decorate(dec1);

  ASSERT_EQ(1, callCount) << "'const T' decoration didn't resolve to 'T'";

  // Test const value
  auto packet2 = factory->NewPacket();

  const int dec2 = 42;
  packet2->Decorate(dec2);

  ASSERT_EQ(2, callCount) << "'const T' decoration didn't resolve to 'T'";
}

TEST_F(AutoFilterCollapseRulesTest, SharedPointerAliasingRules) {
  AutoRequired<AutoPacketFactory> factory;
  bool gen1Called = false;
  bool gen2Called = false;

  *factory += [&gen1Called](AutoPacket&, std::shared_ptr<const int>) {
    gen1Called = true;
  };
  *factory += [&gen2Called](AutoPacket&, int) {
    gen2Called = true;
  };

  auto packet = factory->NewPacket();
  packet->Decorate<int>(55);

  ASSERT_TRUE(gen1Called) << "AutoFilter accepting a shared pointer was not called as expected";
  ASSERT_TRUE(gen2Called) << "AutoFilter accepting a decorated type was not called as expected";
}

class ProducesSharedPointer {
public:
  ProducesSharedPointer(void) :
    m_called(0)
  {}

  int m_called;

  void AutoFilter(std::shared_ptr<int>& output) {
    ++m_called;
    output = std::make_shared<int>(55);
  }
};

TEST_F(AutoFilterCollapseRulesTest, AutoFilterSharedAliasingRules) {
  AutoRequired<ProducesSharedPointer> produces;
  AutoRequired<FilterGen<int>> consumes;
  AutoRequired<AutoPacketFactory> factory;

  // Decorate the packet, verify attribute presence:
  auto packet = factory->NewPacket();
  ASSERT_TRUE(packet->Has<int>()) << "Filter producing a shared pointer of type int did not correctly collapse to the basic int type";
  ASSERT_EQ(55, autowiring::get<0>(consumes->m_args)) << "Filter consuming a shared pointer output was not called as expected";
}

TEST_F(AutoFilterCollapseRulesTest, UnsatisfiableDecoration) {
  AutoRequired<AutoPacketFactory> factory;

  bool bInvoked = false;
  *factory += [&](std::shared_ptr<const Decoration<0>> dec) {
    ASSERT_FALSE(dec) << "Decoration was attached to the packet when it should not have been";
    bInvoked = true;
  };
  *factory += [](const Decoration<0>&) {
    FAIL() << "A filter was invoked when it should have been unsatisfiable";
  };

  // Kick off what should be a launch of the shared pointer-accepting input
  auto packet = factory->NewPacket();
  packet->Decorate(std::shared_ptr<Decoration<0>>());

  // Ensure the correct decoration was invoked
  ASSERT_TRUE(bInvoked) << "AutoFilter was not invoked in an unsatisfiable case as expected";
}

TEST_F(AutoFilterCollapseRulesTest, SharedOutSelfAutoPrev) {
  AutoRequired<AutoPacketFactory> factory;
  AutoCurrentContext()->Initiate();

  auto hello = std::make_shared<std::string>("Hello world!");
  size_t nMsgs = 0;
  *factory += [&](auto_prev<std::string> lastMsg, std::shared_ptr<std::string>& msg) {
    msg = hello;
    if (lastMsg)
      nMsgs++;
  };

  auto packet1 = factory->NewPacket();
  auto packet2 = factory->NewPacket();

  ASSERT_TRUE(packet1->Has<std::string>());
  ASSERT_TRUE(packet2->Has<std::string>());
  ASSERT_EQ(1UL, nMsgs) << "auto_prev was not correctly set when a filter produces a shared_ptr output";
}
<|endoftext|>


<commit_before>// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include <autowiring/CoreThread.h>
#include "TestFixtures/Decoration.hpp"
#include CHRONO_HEADER
#include THREAD_HEADER

class AutoFilterMultiDecorateTest:
  public testing::Test
{
public:
  AutoFilterMultiDecorateTest(void) {
    AutoCurrentContext()->Initiate();
  }

  AutoRequired<AutoPacketFactory> factory;
};

TEST_F(AutoFilterMultiDecorateTest, EnumerateDecorationsTest) {
  auto sample = [](const int* vals []) {};
  AutoFilterDescriptor desc(sample);

  size_t i = 0;
  for (auto* pCur = desc.GetAutoFilterArguments(); *pCur; pCur++)
    i++;

  ASSERT_EQ(1, i) << "AutoFilterDescriptor parsed an incorrect number of arguments from a lambda";
}

TEST_F(AutoFilterMultiDecorateTest, MultiDecorateTest) {
  int called = 0;

  *factory += [&called](int& out) { out = called++; };
  *factory += [&called](int& out) { out = called++; };
  *factory += [&called](const int* vals []) {
    ASSERT_NE(nullptr, vals);
    called++;

    // Guarantee that values were added in the expected order
    int i;
    for (i = 0; vals[i]; i++)
      ASSERT_EQ(i, *(vals[i])) << "Incorrect values were added to the packet";

    // Verify we got the number of values out that we wanted to get out
    ASSERT_EQ(2, i) << "The wrong number of values were added to the packet";
  };
  ASSERT_EQ(0, called) << "Lambda functions were called before expected";

  auto packet = factory->NewPacket();
  ASSERT_EQ(3, called) << "Not all lambda functions were called as expected";
}

TEST_F(AutoFilterMultiDecorateTest, MultiPostHocIntroductionTest) {
  int called = 0;

  *factory += [&called](int& out) { out = called++; };
  *factory += [&called](int& out) { out = called++; };

  // Add a gather step on the packet:
  auto packet = factory->NewPacket();
  *packet += [&called](const int* vals []){
    ASSERT_NE(nullptr, vals);
    called++;
  };

  ASSERT_EQ(3, called) << "Not all lambda functions were called as expected";
}

TEST_F(AutoFilterMultiDecorateTest, UnsatDecTest) {
  AutoRequired<AutoPacketFactory> f;
  *f += [] (const Decoration<0>&, std::string& out) {
    out = "Hello";
  };
  *f += [](const Decoration<1>&, std::string& out) {
    out = "World";
  };
  *f += [](const Decoration<2>&, std::string& out) {
    out = "Crickets";
  };

  int f1 = 0;
  *f += [&](const std::string* args []) {
    for (f1 = 0; *args; args++, f1++);
  };

  int f2 = 0;
  *f += [&](const std::string*const args []) {
    for (f2 = 0; *args; args++, f2++);
  };

  auto packet = f->NewPacket();
  packet->Decorate(Decoration<0>{});
  packet->MarkUnsatisfiable<Decoration<1>>();
  packet->Decorate(Decoration<2>{});

  auto strs = packet->GetAll<std::string>();
  ASSERT_NE(nullptr, strs) << "String datatype not found on multidecorate packet";
  ASSERT_NE(nullptr, strs[0]) << "No strings attached to a multidecorate packet as expected";
  ASSERT_NE(nullptr, strs[1]) << "Expected two strings back, got one";
  ASSERT_EQ(nullptr, strs[2]) << "Expected two strings back, got three";
  ASSERT_EQ("Hello", *strs[0]) << "Entry in multidecorate set was not the expected value";
  ASSERT_EQ("Crickets", *strs[1]) << "Entry in multidecorate set was not the expected value";

  ASSERT_EQ(2, f1) << "const T*[] input decoration count mismatch";
  ASSERT_EQ(2, f2) << "const T*const[] input decoration count mismatch";
}

TEST_F(AutoFilterMultiDecorateTest, ArrayOfSharedPointers) {
  AutoRequired<AutoPacketFactory> f;
  *f += [](const Decoration<0>&, std::string& out) {
    out = "Hello";
  };
  *f += [](const Decoration<1>&, std::string& out) {
    out = "World";
  };

  int f1 = 0;
  *f += [&](std::shared_ptr<const std::string> args []) {
    for (f1 = 0; *args; args++, f1++);
  };

  int f2 = 0;
  *f += [&](const std::shared_ptr<const std::string> args []) {
    for (f2 = 0; *args; args++, f2++);
  };

  auto packet = f->NewPacket();
  packet->Decorate(Decoration<0>{});
  packet->Decorate(Decoration<1>{});

  ASSERT_EQ(2, f1) << "shared_ptr[] input decoration count mismatch";
  ASSERT_EQ(2, f2) << "const shared_ptr[] input decoration count mismatch";
}

class SameOutputTwiceWithInput {
public:
  void AutoFilter(Decoration<0>& one, Decoration<0>& two) {
    one.i = 101;
    two.i = 102;
  }
};

static_assert(has_unambiguous_autofilter<SameOutputTwiceWithInput>::value, "AutoFilter appears to be ambiguous on a multi-out type");
static_assert(has_autofilter<SameOutputTwiceWithInput>::value, "AutoFilter not detected on a multi-out class");

TEST_F(AutoFilterMultiDecorateTest, SingleFunctionDoubleOutput) {
  AutoRequired<AutoPacketFactory> factory;
  AutoCurrentContext()->Initiate();

  AutoRequired<SameOutputTwiceWithInput>();
  *factory += [](Decoration<0>& one, Decoration<0>& two) {
    ASSERT_NE(&one, &two) << "When the same decoration type is mentioned more than once in a signature, the two outputs should be distinct";
    one.i = 1;
    two.i = 2;
  };

  size_t nArgs = 0;
  *factory += [&] (const Decoration<0>* args []) {
    while (*args++)
      nArgs++;
  };

  factory->NewPacket();
  ASSERT_EQ(4UL, nArgs) << "Two filters that multiply attach decorations did not correctly do so";
}

<commit_msg>Add a test of multi-in with single-in decoration<commit_after>// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include <autowiring/CoreThread.h>
#include "TestFixtures/Decoration.hpp"
#include CHRONO_HEADER
#include THREAD_HEADER

class AutoFilterMultiDecorateTest:
  public testing::Test
{
public:
  AutoFilterMultiDecorateTest(void) {
    AutoCurrentContext()->Initiate();
  }

  AutoRequired<AutoPacketFactory> factory;
};

TEST_F(AutoFilterMultiDecorateTest, EnumerateDecorationsTest) {
  auto sample = [](const int* vals []) {};
  AutoFilterDescriptor desc(sample);

  size_t i = 0;
  for (auto* pCur = desc.GetAutoFilterArguments(); *pCur; pCur++)
    i++;

  ASSERT_EQ(1, i) << "AutoFilterDescriptor parsed an incorrect number of arguments from a lambda";
}

TEST_F(AutoFilterMultiDecorateTest, MultiDecorateTest) {
  int called = 0;

  *factory += [&called](int& out) { out = called++; };
  *factory += [&called](int& out) { out = called++; };
  *factory += [&called](const int* vals []) {
    ASSERT_NE(nullptr, vals);
    called++;

    // Guarantee that values were added in the expected order
    int i;
    for (i = 0; vals[i]; i++)
      ASSERT_EQ(i, *(vals[i])) << "Incorrect values were added to the packet";

    // Verify we got the number of values out that we wanted to get out
    ASSERT_EQ(2, i) << "The wrong number of values were added to the packet";
  };
  ASSERT_EQ(0, called) << "Lambda functions were called before expected";

  auto packet = factory->NewPacket();
  ASSERT_EQ(3, called) << "Not all lambda functions were called as expected";
}

TEST_F(AutoFilterMultiDecorateTest, MultiWithSingleDecorateTest) {
  int called = 0;
  float f_called = 0.0f;

  *factory += [&called](int& out) { out = called++; };
  *factory += [&called](int& out) { out = called++; };
  *factory += [&f_called](float& out) { out = ++f_called; };
  *factory += [&called](const int* vals [], const float& val) {
    ASSERT_NE(nullptr, vals);
    called++;

    // Guarantee that values were added in the expected order
    int i;
    for (i = 0; vals[i]; i++)
      ASSERT_EQ(i, *(vals[i])) << "Incorrect values were added to the packet";

    // Verify we got the number of values out that we wanted to get out
    ASSERT_EQ(2, i) << "The wrong number of values were added to the packet";
    ASSERT_EQ(1.0f, val) << "The wrong number of val were added to the packet";
  };
  ASSERT_EQ(0, called) << "Lambda functions were called before expected";

  auto packet = factory->NewPacket();
  ASSERT_EQ(3, called) << "Not all lambda functions were called as expected";
}

TEST_F(AutoFilterMultiDecorateTest, MultiPostHocIntroductionTest) {
  int called = 0;

  *factory += [&called](int& out) { out = called++; };
  *factory += [&called](int& out) { out = called++; };

  // Add a gather step on the packet:
  auto packet = factory->NewPacket();
  *packet += [&called](const int* vals []){
    ASSERT_NE(nullptr, vals);
    called++;
  };

  ASSERT_EQ(3, called) << "Not all lambda functions were called as expected";
}

TEST_F(AutoFilterMultiDecorateTest, UnsatDecTest) {
  AutoRequired<AutoPacketFactory> f;
  *f += [] (const Decoration<0>&, std::string& out) {
    out = "Hello";
  };
  *f += [](const Decoration<1>&, std::string& out) {
    out = "World";
  };
  *f += [](const Decoration<2>&, std::string& out) {
    out = "Crickets";
  };

  int f1 = 0;
  *f += [&](const std::string* args []) {
    for (f1 = 0; *args; args++, f1++);
  };

  int f2 = 0;
  *f += [&](const std::string*const args []) {
    for (f2 = 0; *args; args++, f2++);
  };

  auto packet = f->NewPacket();
  packet->Decorate(Decoration<0>{});
  packet->MarkUnsatisfiable<Decoration<1>>();
  packet->Decorate(Decoration<2>{});

  auto strs = packet->GetAll<std::string>();
  ASSERT_NE(nullptr, strs) << "String datatype not found on multidecorate packet";
  ASSERT_NE(nullptr, strs[0]) << "No strings attached to a multidecorate packet as expected";
  ASSERT_NE(nullptr, strs[1]) << "Expected two strings back, got one";
  ASSERT_EQ(nullptr, strs[2]) << "Expected two strings back, got three";
  ASSERT_EQ("Hello", *strs[0]) << "Entry in multidecorate set was not the expected value";
  ASSERT_EQ("Crickets", *strs[1]) << "Entry in multidecorate set was not the expected value";

  ASSERT_EQ(2, f1) << "const T*[] input decoration count mismatch";
  ASSERT_EQ(2, f2) << "const T*const[] input decoration count mismatch";
}

TEST_F(AutoFilterMultiDecorateTest, ArrayOfSharedPointers) {
  AutoRequired<AutoPacketFactory> f;
  *f += [](const Decoration<0>&, std::string& out) {
    out = "Hello";
  };
  *f += [](const Decoration<1>&, std::string& out) {
    out = "World";
  };

  int f1 = 0;
  *f += [&](std::shared_ptr<const std::string> args []) {
    for (f1 = 0; *args; args++, f1++);
  };

  int f2 = 0;
  *f += [&](const std::shared_ptr<const std::string> args []) {
    for (f2 = 0; *args; args++, f2++);
  };

  auto packet = f->NewPacket();
  packet->Decorate(Decoration<0>{});
  packet->Decorate(Decoration<1>{});

  ASSERT_EQ(2, f1) << "shared_ptr[] input decoration count mismatch";
  ASSERT_EQ(2, f2) << "const shared_ptr[] input decoration count mismatch";
}

class SameOutputTwiceWithInput {
public:
  void AutoFilter(Decoration<0>& one, Decoration<0>& two) {
    one.i = 101;
    two.i = 102;
  }
};

static_assert(has_unambiguous_autofilter<SameOutputTwiceWithInput>::value, "AutoFilter appears to be ambiguous on a multi-out type");
static_assert(has_autofilter<SameOutputTwiceWithInput>::value, "AutoFilter not detected on a multi-out class");

TEST_F(AutoFilterMultiDecorateTest, SingleFunctionDoubleOutput) {
  AutoRequired<AutoPacketFactory> factory;
  AutoCurrentContext()->Initiate();

  AutoRequired<SameOutputTwiceWithInput>();
  *factory += [](Decoration<0>& one, Decoration<0>& two) {
    ASSERT_NE(&one, &two) << "When the same decoration type is mentioned more than once in a signature, the two outputs should be distinct";
    one.i = 1;
    two.i = 2;
  };

  size_t nArgs = 0;
  *factory += [&] (const Decoration<0>* args []) {
    while (*args++)
      nArgs++;
  };

  factory->NewPacket();
  ASSERT_EQ(4UL, nArgs) << "Two filters that multiply attach decorations did not correctly do so";
}

<|endoftext|>


<commit_before>#ifndef BONEFISH_SERIALIZATION_WAMP_SERIALIZER_TYPE_HPP
#define BONEFISH_SERIALIZATION_WAMP_SERIALIZER_TYPE_HPP

#include <cstddef>
#include <string>
#include <type_traits>

namespace bonefish {

enum class wamp_serializer_type : unsigned
{
    MSGPACK
};

} // namespace bonefish

namespace std {

template<> struct hash<bonefish::wamp_serializer_type>
{
    size_t operator()(const bonefish::wamp_serializer_type& type) const
    {
        return hash<std::underlying_type<bonefish::wamp_serializer_type>::type>()(
                static_cast<std::underlying_type<bonefish::wamp_serializer_type>::type>(type));
    }
};

} // namespace std

#endif // BONEFISH_SERIALIZATION_WAMP_SERIALIZER_TYPE_HPP
<commit_msg>Explicitly include functional header to bring in hash definitions<commit_after>#ifndef BONEFISH_SERIALIZATION_WAMP_SERIALIZER_TYPE_HPP
#define BONEFISH_SERIALIZATION_WAMP_SERIALIZER_TYPE_HPP

#include <cstddef>
#include <functional>
#include <string>
#include <type_traits>

namespace bonefish {

enum class wamp_serializer_type : unsigned
{
    MSGPACK
};

} // namespace bonefish

namespace std {

template<> struct hash<bonefish::wamp_serializer_type>
{
    size_t operator()(const bonefish::wamp_serializer_type& type) const
    {
        return hash<std::underlying_type<bonefish::wamp_serializer_type>::type>()(
                static_cast<std::underlying_type<bonefish::wamp_serializer_type>::type>(type));
    }
};

} // namespace std

#endif // BONEFISH_SERIALIZATION_WAMP_SERIALIZER_TYPE_HPP
<|endoftext|>


<commit_before>#include "EEPROMManager.h"
#include <EEPROM.h>
#include "config.h"
#include "EEPROMAnything.h"

EEPROMManager::CEEPROMManager() {
	return;
}

void CEEPROMManager::begin() {
	//	has the EEPROM been initialized?
	uint8_t flag = EEPROM.read(EEPROM_ADDR_WAS_INIT);
	if( flag != EEPROM_ADDR_WAS_INIT ) {
		//	we have not been initialized so set bit fields to 0 (no custom thresholds);
		EEPROM_writeAnything(EEPROM_ADDR_TCH_THRSH,(uint16_t)0);
		EEPROM_writeAnything(EEPROM_ADDR_REL_THRSH,(uint16_t)0);
	}
}

void EEPROMManager::writeTouchThreshold(uint8_t sensor, uint16_t threshold) {
	uint32_t addr = EEPROM_ADDR_TCH_BASE + 2 * sensor;
	EEPROM_writeAnything(addr,threshold);

	//	manage flags
	uint16_t flags = EEPROM_readAnything(EEPROM_ADDR_TCH_THRSH, _dummy_int);
	flags |= sensor;
	EEPROM_writeAnything(EEPROM_ADDR_TCH_THRSH, flags);
}

void CEEPROMManager::writeReleaseThreshold(uint8_t sensor, uint16_t threshold) {
	uint32_t addr = EEPROM_ADDR_REL_BASE + 2 * sensor;
	EEPROM_writeAnything(addr,threshold);

	//	manage flags
	uint16_t flags = EEPROM_readAnything(EEPROM_ADDR_REL_THRSH, _dummy_int);
	flags |= sensor;
	EEPROM_writeAnything(EEPROM_ADDR_REL_THRSH, flags);
}

uint16_t EEPROMManager::readTouchThreshold(uint8_t sensor) {
	uint32_t addr = EEPROM_ADDR_TCH_BASE + 2 * sensor;
	uint16_t val = EEPROM_readAnything(addr, _dummy_int);
	return val;
}

uint16_t EEPROMManager::readReleaseThreshold(uint8_t sensor) {
	uint32_t addr = EEPROM_ADDR_REL_BASE + 2 * sensor;
	uint16_t val = EEPROM_readAnything(addr, _dummy_int);
	return val;
}

uint16_t EEPROMManager::customTouchThresholdFlags() {
	uint16_t val = EEPROM_readAnything(EEPROM_ADDR_TCH_THRSH, _dummy_int);
	return val;
}

uint16_t EEPROMManager::customReleaseThresholdFlags() {
	uint16_t val = EEPROM_readAnything(EEPROM_ADDR_REL_THRSH, _dummy_int);
	return val;
}

<commit_msg>Fixed name of constructor and other namespaces<commit_after>#include "EEPROMManager.h"
#include <EEPROM.h>
#include "config.h"
#include "EEPROMAnything.h"

EEPROMManager::EEPROMManager() {
	return;
}

void EEPROMManager::begin() {
	//	has the EEPROM been initialized?
	uint8_t flag = EEPROM.read(EEPROM_ADDR_WAS_INIT);
	if( flag != EEPROM_ADDR_WAS_INIT ) {
		//	we have not been initialized so set bit fields to 0 (no custom thresholds);
		EEPROM_writeAnything(EEPROM_ADDR_TCH_THRSH,(uint16_t)0);
		EEPROM_writeAnything(EEPROM_ADDR_REL_THRSH,(uint16_t)0);
	}
}

void EEPROMManager::writeTouchThreshold(uint8_t sensor, uint16_t threshold) {
	uint32_t addr = EEPROM_ADDR_TCH_BASE + 2 * sensor;
	EEPROM_writeAnything(addr,threshold);

	//	manage flags
	uint16_t flags = EEPROM_readAnything(EEPROM_ADDR_TCH_THRSH, _dummy_int);
	flags |= sensor;
	EEPROM_writeAnything(EEPROM_ADDR_TCH_THRSH, flags);
}

void EEPROMManager::writeReleaseThreshold(uint8_t sensor, uint16_t threshold) {
	uint32_t addr = EEPROM_ADDR_REL_BASE + 2 * sensor;
	EEPROM_writeAnything(addr,threshold);

	//	manage flags
	uint16_t flags = EEPROM_readAnything(EEPROM_ADDR_REL_THRSH, _dummy_int);
	flags |= sensor;
	EEPROM_writeAnything(EEPROM_ADDR_REL_THRSH, flags);
}

uint16_t EEPROMManager::readTouchThreshold(uint8_t sensor) {
	uint32_t addr = EEPROM_ADDR_TCH_BASE + 2 * sensor;
	uint16_t val = EEPROM_readAnything(addr, _dummy_int);
	return val;
}

uint16_t EEPROMManager::readReleaseThreshold(uint8_t sensor) {
	uint32_t addr = EEPROM_ADDR_REL_BASE + 2 * sensor;
	uint16_t val = EEPROM_readAnything(addr, _dummy_int);
	return val;
}

uint16_t EEPROMManager::customTouchThresholdFlags() {
	uint16_t val = EEPROM_readAnything(EEPROM_ADDR_TCH_THRSH, _dummy_int);
	return val;
}

uint16_t EEPROMManager::customReleaseThresholdFlags() {
	uint16_t val = EEPROM_readAnything(EEPROM_ADDR_REL_THRSH, _dummy_int);
	return val;
}

<|endoftext|>


<commit_before>#include "LIBGPU.H"

#define GL_GLEXT_PROTOTYPES 1
#include "GL/glew.h"
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_opengl_glext.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <LIBETC.H>

#include "EMULATOR.H"
#include "EMULATOR_GLOBALS.H"

unsigned short vram[1024 * 512];
DISPENV word_33BC;
int dword_3410 = 0;
char byte_3352 = 0;

void* off_3348[]=
{
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

int ClearImage(RECT16* rect, u_char r, u_char g, u_char b)
{
	for (int y = rect->y; y < 512; y++)
	{
		for (int x = rect->x; x < 1024; x++)
		{
			unsigned short* pixel = vram + (y * 1024 + x);

			if (x >= rect->x && x < rect->x + rect->w &&
				y >= rect->y && y < rect->y + rect->h)
			{
				pixel[0] = 1 << 15 | ((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3));
			}
		}
	}

	return 0;
}

int DrawSync(int mode)
{
	return 0;
}

int LoadImagePSX(RECT16* rect, u_long* p)
{
	unsigned short* dst = (unsigned short*)p;

	for (int y = rect->y; y < 512; y++)
	{
		for (int x = rect->x; x < 1024; x++)
		{
			unsigned short* src = vram + (y * 1024 + x);

			if (x >= rect->x && x < rect->x + rect->w &&
				y >= rect->y && y < rect->y + rect->h)
			{
				src[0] = *dst++;

				//pixel[0] = 1 << 15 | ((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3));
			}
		}
	}

#if _DEBUG
	FILE* f = fopen("VRAM.BIN", "wb");
	fwrite(&vram[0], 1, 1024 * 512 * 2, f);
	fclose(f);
#endif

	return 0;
}

int ResetGraph(int mode)
{
	return 0;
}

int SetGraphDebug(int level)
{
	return 0;
}

int StoreImage(RECT16 * RECT16, u_long * p)
{
	return 0;
}

u_long * ClearOTag(u_long * ot, int n)
{
	assert(0);
	return 0;
}

u_long* ClearOTagR(u_long* ot, int n)
{
	memset(ot, 0, n * sizeof(int));
	return 0;
}

void SetDispMask(int mask)
{
}

DISPENV* GetDispEnv(DISPENV* env)//(F)
{
	memcpy(env, &word_33BC, sizeof(DISPENV));
	return env;
}

DISPENV* PutDispEnv(DISPENV* env)//To Finish
{
	memcpy((char*)&word_33BC, env, sizeof(DISPENV));
	return 0;
}

DISPENV* SetDefDispEnv(DISPENV* env, int x, int y, int w, int h)//(F)
{
	env->disp.x = x;
	env->disp.y = y;
	env->disp.w = w;
	env->screen.x = 0;
	env->screen.y = 0;
	env->screen.w = 0;
	env->screen.h = 0;
	env->isrgb24 = 0;
	env->isinter = 0;
	env->pad1 = 0;
	env->pad0 = 0;
	env->disp.h = h;
	return 0;
}

DRAWENV* PutDrawEnv(DRAWENV* env)
{
	return NULL;
}

DRAWENV* SetDefDrawEnv(DRAWENV* env, int x, int y, int w, int h)//(F)
{
	env->clip.x = x;
	env->clip.y = y;
	env->clip.w = w;
	env->tw.x = 0;
	env->tw.y = 0;
	env->tw.w = 0;
	env->tw.h = 0;
	env->r0 = 0;
	env->g0 = 0;
	env->b0 = 0;
	env->dtd = 1;
	env->clip.h = h;

	if (GetVideoMode() == 0)
	{
		env->dfe = h < 0x121 ? 1 : 0;
	}
	else
	{
		env->dfe = h < 0x101 ? 1 : 0;
	}

	env->ofs[0] = x;
	env->ofs[1] = y;

	env->tpage = 10;
	env->isbg = 0;

	return env;
}

u_long DrawSyncCallback(void(*func)(void))
{

	return u_long();
}

int test = 0;

void DrawOTagEnv(u_long* p, DRAWENV* env)
{
	P_TAG* pTag = (P_TAG*)*p;
	if (pTag != NULL)
	{
		GLuint fbo = 0;
		glGenFramebuffers(1, &fbo);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo);

		glEnable(GL_TEXTURE_2D);
		glGenTextures(1, &vramTexture);
		glBindTexture(GL_TEXTURE_2D, vramTexture);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 512, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, &vram[0]);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

		glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, vramTexture, 0);
		GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
		glDrawBuffers(1, DrawBuffers);

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glOrtho(0, 1024, 0, 512, -1, 1);
		glViewport(0, 0, 1024, 512);

		while (1)
		{
			switch (pTag->code)
			{
			case 0x2C:
			{
				POLY_FT4* poly = (POLY_FT4*)pTag;

				///@FIXME im unsure if this is stable.
				int tpage = (poly->tpage & 0x1F) / 4 + 1;
				int x = (tpage * 256) % 1024 - 256;
				int y = (tpage / 4) * 256;

				glBegin(GL_QUADS);
				glTranslatef(1.0f, 0.0f, 0.0f);
				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u0 + x)), 1.0f / (512.0f / (float)(poly->v0 + y)));
				glVertex2f(poly->x0, poly->y0);

				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u1 + x)), 1.0f / (512.0f / (float)(poly->v1 + y)));
				glVertex2f(poly->x1, poly->y1);

				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u3 + x)), 1.0f / (512.0f / (float)(poly->v3 + y)));
				glVertex2f(poly->x3, poly->y3);

				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u2 + x)), 1.0f / (512.0f / (float)(poly->v2 + y)));
				glVertex2f(poly->x2, poly->y2);

				glEnd();
				break;
			}
			default:
				//Unhandled poly
				assert(0);
				break;
			}

			if (pTag->addr == 0)
			{
				break;
			}

			pTag = (P_TAG*)((uintptr_t)pTag - ((pTag->len * 4) + 4));
		}

		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		{
			printf("Frame buffer error!\n");
		}

		unsigned short* pixels = new unsigned short[1024 * 512];
		memset(&pixels[0], 0, 1024 * 512 * 2);
		glReadPixels(0, 0, 1024, 512, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, &pixels[0]);
		memcpy(&vram[0], pixels, 1024 * 512 * 2);

#if _DEBUG
		FILE* f = fopen("VRAM2.TGA", "wb");
		unsigned char TGAheader[12] = { 0,0,2,0,0,0,0,0,0,0,0,0 };
		unsigned char header[6] = { 1024 % 256, 1024 / 256, 512 % 256, 512 / 256,16,0 };
		fwrite(TGAheader, sizeof(unsigned char), 12, f);
		fwrite(header, sizeof(unsigned char), 6, f);
		fwrite(pixels, sizeof(char), 1024 * 512 * 2, f);
		fclose(f);
#endif
		delete[] pixels;
		glDeleteFramebuffers(1, &fbo);
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glViewport(0, 0, screenWidth, screenHeight);
		glPopMatrix();
		glDeleteTextures(1, &vramTexture);
	}
}
<commit_msg>Remove invalid header<commit_after>#include "LIBGPU.H"

#define GL_GLEXT_PROTOTYPES 1
#include <GL/glew.h>
#include <SDL.h>
#include <SDL_opengl.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <LIBETC.H>

#include "EMULATOR.H"
#include "EMULATOR_GLOBALS.H"

unsigned short vram[1024 * 512];
DISPENV word_33BC;
int dword_3410 = 0;
char byte_3352 = 0;

void* off_3348[]=
{
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

int ClearImage(RECT16* rect, u_char r, u_char g, u_char b)
{
	for (int y = rect->y; y < 512; y++)
	{
		for (int x = rect->x; x < 1024; x++)
		{
			unsigned short* pixel = vram + (y * 1024 + x);

			if (x >= rect->x && x < rect->x + rect->w &&
				y >= rect->y && y < rect->y + rect->h)
			{
				pixel[0] = 1 << 15 | ((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3));
			}
		}
	}

	return 0;
}

int DrawSync(int mode)
{
	return 0;
}

int LoadImagePSX(RECT16* rect, u_long* p)
{
	unsigned short* dst = (unsigned short*)p;

	for (int y = rect->y; y < 512; y++)
	{
		for (int x = rect->x; x < 1024; x++)
		{
			unsigned short* src = vram + (y * 1024 + x);

			if (x >= rect->x && x < rect->x + rect->w &&
				y >= rect->y && y < rect->y + rect->h)
			{
				src[0] = *dst++;

				//pixel[0] = 1 << 15 | ((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3));
			}
		}
	}

#if _DEBUG
	FILE* f = fopen("VRAM.BIN", "wb");
	fwrite(&vram[0], 1, 1024 * 512 * 2, f);
	fclose(f);
#endif

	return 0;
}

int ResetGraph(int mode)
{
	return 0;
}

int SetGraphDebug(int level)
{
	return 0;
}

int StoreImage(RECT16 * RECT16, u_long * p)
{
	return 0;
}

u_long * ClearOTag(u_long * ot, int n)
{
	assert(0);
	return 0;
}

u_long* ClearOTagR(u_long* ot, int n)
{
	memset(ot, 0, n * sizeof(int));
	return 0;
}

void SetDispMask(int mask)
{
}

DISPENV* GetDispEnv(DISPENV* env)//(F)
{
	memcpy(env, &word_33BC, sizeof(DISPENV));
	return env;
}

DISPENV* PutDispEnv(DISPENV* env)//To Finish
{
	memcpy((char*)&word_33BC, env, sizeof(DISPENV));
	return 0;
}

DISPENV* SetDefDispEnv(DISPENV* env, int x, int y, int w, int h)//(F)
{
	env->disp.x = x;
	env->disp.y = y;
	env->disp.w = w;
	env->screen.x = 0;
	env->screen.y = 0;
	env->screen.w = 0;
	env->screen.h = 0;
	env->isrgb24 = 0;
	env->isinter = 0;
	env->pad1 = 0;
	env->pad0 = 0;
	env->disp.h = h;
	return 0;
}

DRAWENV* PutDrawEnv(DRAWENV* env)
{
	return NULL;
}

DRAWENV* SetDefDrawEnv(DRAWENV* env, int x, int y, int w, int h)//(F)
{
	env->clip.x = x;
	env->clip.y = y;
	env->clip.w = w;
	env->tw.x = 0;
	env->tw.y = 0;
	env->tw.w = 0;
	env->tw.h = 0;
	env->r0 = 0;
	env->g0 = 0;
	env->b0 = 0;
	env->dtd = 1;
	env->clip.h = h;

	if (GetVideoMode() == 0)
	{
		env->dfe = h < 0x121 ? 1 : 0;
	}
	else
	{
		env->dfe = h < 0x101 ? 1 : 0;
	}

	env->ofs[0] = x;
	env->ofs[1] = y;

	env->tpage = 10;
	env->isbg = 0;

	return env;
}

u_long DrawSyncCallback(void(*func)(void))
{

	return u_long();
}

int test = 0;

void DrawOTagEnv(u_long* p, DRAWENV* env)
{
	P_TAG* pTag = (P_TAG*)*p;
	if (pTag != NULL)
	{
		GLuint fbo = 0;
		glGenFramebuffers(1, &fbo);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo);

		glEnable(GL_TEXTURE_2D);
		glGenTextures(1, &vramTexture);
		glBindTexture(GL_TEXTURE_2D, vramTexture);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 512, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, &vram[0]);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

		glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, vramTexture, 0);
		GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
		glDrawBuffers(1, DrawBuffers);

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glOrtho(0, 1024, 0, 512, -1, 1);
		glViewport(0, 0, 1024, 512);

		while (1)
		{
			switch (pTag->code)
			{
			case 0x2C:
			{
				POLY_FT4* poly = (POLY_FT4*)pTag;

				///@FIXME im unsure if this is stable.
				int tpage = (poly->tpage & 0x1F) / 4 + 1;
				int x = (tpage * 256) % 1024 - 256;
				int y = (tpage / 4) * 256;

				glBegin(GL_QUADS);
				glTranslatef(1.0f, 0.0f, 0.0f);
				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u0 + x)), 1.0f / (512.0f / (float)(poly->v0 + y)));
				glVertex2f(poly->x0, poly->y0);

				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u1 + x)), 1.0f / (512.0f / (float)(poly->v1 + y)));
				glVertex2f(poly->x1, poly->y1);

				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u3 + x)), 1.0f / (512.0f / (float)(poly->v3 + y)));
				glVertex2f(poly->x3, poly->y3);

				//glColor3ub(poly->r0, poly->g0, poly->b0);
				glTexCoord2f(1.0f / (1024.0f / (float)(poly->u2 + x)), 1.0f / (512.0f / (float)(poly->v2 + y)));
				glVertex2f(poly->x2, poly->y2);

				glEnd();
				break;
			}
			default:
				//Unhandled poly
				assert(0);
				break;
			}

			if (pTag->addr == 0)
			{
				break;
			}

			pTag = (P_TAG*)((uintptr_t)pTag - ((pTag->len * 4) + 4));
		}

		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		{
			printf("Frame buffer error!\n");
		}

		unsigned short* pixels = new unsigned short[1024 * 512];
		memset(&pixels[0], 0, 1024 * 512 * 2);
		glReadPixels(0, 0, 1024, 512, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, &pixels[0]);
		memcpy(&vram[0], pixels, 1024 * 512 * 2);

#if _DEBUG
		FILE* f = fopen("VRAM2.TGA", "wb");
		unsigned char TGAheader[12] = { 0,0,2,0,0,0,0,0,0,0,0,0 };
		unsigned char header[6] = { 1024 % 256, 1024 / 256, 512 % 256, 512 / 256,16,0 };
		fwrite(TGAheader, sizeof(unsigned char), 12, f);
		fwrite(header, sizeof(unsigned char), 6, f);
		fwrite(pixels, sizeof(char), 1024 * 512 * 2, f);
		fclose(f);
#endif
		delete[] pixels;
		glDeleteFramebuffers(1, &fbo);
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glViewport(0, 0, screenWidth, screenHeight);
		glPopMatrix();
		glDeleteTextures(1, &vramTexture);
	}
}
<|endoftext|>