<commit_before>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//

#include <GL/glew.h>

#include <BALL/VIEW/RENDERING/glRenderWindow.h>
#include <BALL/VIEW/WIDGETS/scene.h>
#include <BALL/COMMON/logStream.h>
#include <BALL/VIEW/KERNEL/common.h>

#include <QtCore/QEvent>
#include <QtGui/QPaintEvent>

//#define USE_GLPAINTPIXELS
#undef USE_GLPAINTPIXELS

namespace BALL
{
	namespace VIEW
	{
	  QGLFormat GLRenderWindow::gl_format_(
				QGL::DepthBuffer 		 |
#ifndef BALL_OS_DARWIN
				QGL::StereoBuffers 	 |
#endif
				QGL::DoubleBuffer 	 |
				QGL::DirectRendering |
				QGL::SampleBuffers   |
				QGL::StencilBuffer);

		GLRenderWindow::GLRenderWindow()
			: QGLWidget(gl_format_),
			  stereo_delta_(0.),
			  m_screenTexID(0),
			  FB_TEXTURE_TARGET(GL_TEXTURE_2D),
			  FB_TEXTURE_FORMAT(GL_RGB),
			  FB_INTERNAL_TEXTURE_FORMAT(GL_RGB),
			  FB_TEXTURE_DATATYPE(GL_FLOAT),
			  ignore_events_(false),
			  down_sampling_factor_(1.)
		{
			// we will swap buffers manually in the scene for synchronization
			setAutoBufferSwap(false);
			setAutoFillBackground(false);
		}

		GLRenderWindow::GLRenderWindow(QWidget* parent_widget, const char* /*name*/, Qt::WFlags w_flags)
			: QGLWidget(gl_format_, parent_widget, (QGLWidget*)0, w_flags),
			  stereo_delta_(0.),
			  m_screenTexID(0),
			  FB_TEXTURE_TARGET(GL_TEXTURE_2D),
			  FB_TEXTURE_FORMAT(GL_RGB),
			  FB_INTERNAL_TEXTURE_FORMAT(GL_RGB),
			  FB_TEXTURE_DATATYPE(GL_FLOAT),
			  ignore_events_(false),
			  down_sampling_factor_(1)
		{
			if (!QGLWidget::isValid())
			{
				Log.error() << "QGLWidget is not valid in Scene!" << std::endl;
			}
			// we will swap buffers manually in the scene for synchronization
			setAutoBufferSwap(false);
			setAutoFillBackground(false);
		}

		GLRenderWindow::GLRenderWindow(const GLRenderWindow& window, QWidget* parent_widget, const char* /*name*/, Qt::WFlags w_flags)
			: QGLWidget(gl_format_, parent_widget, reinterpret_cast<QGLWidget const*>(&window), w_flags),
			  stereo_delta_(0.),
			  m_screenTexID(0),
			  FB_TEXTURE_TARGET(GL_TEXTURE_2D),
			  FB_TEXTURE_FORMAT(GL_RGB),
			  FB_INTERNAL_TEXTURE_FORMAT(GL_RGB),
			  FB_TEXTURE_DATATYPE(GL_FLOAT),
			  ignore_events_(false),
			  down_sampling_factor_(1.)
		{
			// we will swap buffers manually in the scene for synchronization
			setAutoBufferSwap(false);
			setAutoFillBackground(false);
		}

		GLRenderWindow::~GLRenderWindow()
		{
			deleteTexture();
		}

		bool GLRenderWindow::init()
		{
			checkGL();

			// TODO: is this necessary?
			if (!format().rgba())
				Log.error() << "No RGBA mode for OpenGl available." << std::endl;

			RenderWindow::init();
			bool result = false;

			FB_TEXTURE_TARGET = GL_TEXTURE_2D;

			if(m_fmt.getPixelFormat() == PixelFormat::RGBF_96)
			{
				FB_INTERNAL_TEXTURE_FORMAT = GL_RGB;
				FB_TEXTURE_FORMAT = GL_RGB;
				FB_TEXTURE_DATATYPE = GL_FLOAT;
				result = true;
			}
			else
			{
				if(m_fmt.getPixelFormat() == PixelFormat::RGBA_32)
				{
					FB_INTERNAL_TEXTURE_FORMAT = GL_RGBA;
					FB_TEXTURE_FORMAT = GL_RGBA;
					FB_TEXTURE_DATATYPE = GL_UNSIGNED_BYTE;
				}
				result = true;
			}
			return result;
		}

		bool GLRenderWindow::resize(const unsigned int width, const unsigned int height)
		{
			if(!RenderWindow::resize((int)ceil(width/down_sampling_factor_), (int)ceil(height/down_sampling_factor_)))
			{
				return false;
			}
			createTexture((int)ceil(width/down_sampling_factor_), (int)ceil(height/down_sampling_factor_));

			QGLWidget::resize(width, height);

			return true;
		}

		void GLRenderWindow::refresh()
		{
			RenderWindow::refresh();

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			glPushAttrib(GL_TEXTURE_BIT);
			glPushAttrib(GL_DEPTH_BUFFER_BIT);

#ifdef USE_GLPAINTPIXELS
			glDrawPixels(m_fmt.getWidth(), m_fmt.getHeight(), FB_TEXTURE_FORMAT, FB_TEXTURE_DATATYPE, m_pixels.get());
#else
			glBindTexture(FB_TEXTURE_TARGET, m_screenTexID);
			glTexSubImage2D(FB_TEXTURE_TARGET, 0, 0, 0, m_fmt.getWidth(), m_fmt.getHeight(),
					FB_TEXTURE_FORMAT, FB_TEXTURE_DATATYPE, m_pixels.get());

			glEnable(FB_TEXTURE_TARGET);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#endif

			glDisable(GL_DEPTH_TEST);
			glMatrixMode(GL_PROJECTION);
			glPushMatrix();

			glLoadIdentity();

			glPushAttrib(GL_VIEWPORT_BIT);
#ifndef USE_GLPAINTPIXELS
			glViewport(0, 0, down_sampling_factor_*m_fmt.getWidth(), down_sampling_factor_*m_fmt.getHeight());
			float aspectRatio = static_cast<float>(m_fmt.getWidth()) / m_fmt.getHeight();
			glOrtho(-aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f);

			float origWidth = static_cast<float>(m_fmt.getWidth());
			float newWidth = (origWidth + fabs(stereo_delta_));
			float newRatio = origWidth / newWidth;
			float deltaRatio = 1. - newRatio;
			if (newRatio != 1.)
			{
				std::cout << newRatio << std::endl;
			}

			glBegin(GL_QUADS);

			if (stereo_delta_ <= 0.)
			{
				glTexCoord2f(0.0f+deltaRatio, 0.0f);
				glVertex2f(-aspectRatio, -1.0f);

				glTexCoord2f(1.0f, 0.0f );
				glVertex2f(aspectRatio, -1.0f);

				glTexCoord2f(1.0f, 1.0f );
				glVertex2f(aspectRatio, 1.0f);

				glTexCoord2f(0.0f+deltaRatio, 1.0f);
				glVertex2f(-aspectRatio, 1.0f);
			}
			else
			{
				glTexCoord2f(0.0f, 0.0f);
				glVertex2f(-aspectRatio, -1.0f);

				glTexCoord2f(1.0f-deltaRatio, 0.0f );
				glVertex2f(aspectRatio, -1.0f);

				glTexCoord2f(1.0f-deltaRatio, 1.0f );
				glVertex2f(aspectRatio, 1.0f);

				glTexCoord2f(0.0f, 1.0f);
				glVertex2f(-aspectRatio, 1.0f);
			}

			glEnd();
#endif
			glPopAttrib();

			glPopMatrix();
			glMatrixMode(GL_MODELVIEW);

			glPopAttrib();
			glPopAttrib();
		}

		void GLRenderWindow::renderText(int x, int y, const String& text, const ColorRGBA& color, Size size)
		{
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			glViewport(0, 0, m_fmt.getWidth(), m_fmt.getHeight());

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			QFont font;
			font.setPixelSize(size);
			font.setBold(true);

			glDisable(GL_LIGHTING);
			glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
			QGLWidget::renderText(x, y, text.c_str(), font);
			glEnable(GL_LIGHTING);
		}

		void GLRenderWindow::renderText(float x, float y, float z, const String& text, const ColorRGBA& color, Size size)
		{
			// TEST!
			return;
			QFont font;
			font.setPixelSize(size);
			font.setBold(true);

			glDisable(GL_LIGHTING);
			glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
			QGLWidget::renderText(x, y, z, text.c_str(), font);
			glEnable(GL_LIGHTING);
		}

		void GLRenderWindow::createTexture(const unsigned int width, const unsigned int height)
		{
			if(m_screenTexID != 0)
			{
				deleteTexture();
			}

			glGenTextures(1, &m_screenTexID);

			glBindTexture(FB_TEXTURE_TARGET, m_screenTexID);

			glTexParameteri(FB_TEXTURE_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(FB_TEXTURE_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			glTexImage2D(FB_TEXTURE_TARGET, 0, FB_INTERNAL_TEXTURE_FORMAT, width, height, 0, FB_TEXTURE_FORMAT, FB_TEXTURE_DATATYPE, NULL);

			glBindTexture(FB_TEXTURE_TARGET, 0);
		}

		void GLRenderWindow::deleteTexture()
		{
			glDeleteTextures(1, &m_screenTexID);
			m_screenTexID = 0;
		}

		bool GLRenderWindow::errorInGL(GLenum& error)
		{
				error = glGetError();
				return (error != GL_NO_ERROR);
		}

		String GLRenderWindow::getGLErrorString(GLenum error)
		{
				String result;
				switch(error)
				{
				case GL_INVALID_ENUM:
						result = "Invalid enumeration value";
						break;
				case GL_INVALID_VALUE:
						result = "Numeric argument out of range";
						break;
				case GL_INVALID_OPERATION:
						result = "Operation illegal in current state";
						break;
				case GL_STACK_OVERFLOW:
						result = "Command would cause stack overflow";
						break;
				case GL_STACK_UNDERFLOW:
						result = "Command would cause stack underflow";
						break;
				case GL_OUT_OF_MEMORY:
						result = "Not enough memory left to execute command";
						break;
				case GL_TABLE_TOO_LARGE:
						result = "The specified table is too large";
						break;
				default:
						result = "Uknown OpenGL error";
				}
				return result;
		}

		void GLRenderWindow::checkGL()
		{
				GLenum err;
				if(errorInGL(err))
				{
					BALL::Log.error() << "Error in OpenGL: " << getGLErrorString(err) << std::endl;
				}
		}

		void GLRenderWindow::customEvent(QEvent* evt)
		{
			switch(static_cast<EventsIDs>(evt->type())) {
				case RENDER_TO_BUFFER_FINISHED_EVENT:
					printf("refreshing\n");
					refresh();
					swapBuffers();
					break;
			}
		}

		void GLRenderWindow::paintEvent(QPaintEvent* e)
		{
			if (!ignore_events_)
			{
				QGLWidget::paintEvent(e);
			}
		}

		void GLRenderWindow::lockGLContext()
		{
			contex_mutex_.lock();
			makeCurrent();
		}

		void GLRenderWindow::unlockGLContext()
		{
			doneCurrent();
			contex_mutex_.unlock();
		}

		void GLRenderWindow::setupStereo(float eye_separation, float focal_length)
		{
				float aperture = 60.;
				float width = static_cast<float>(m_fmt.getWidth());

				//formula according to Paul Bourke
				//http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/stereographics/stereorender/
				stereo_delta_ = (fabs(eye_separation) * width) / (focal_length * tan(Angle(aperture, false).toRadian()));
				std::cout << stereo_delta_ << std::endl;
		}
	} // namespace VIEW
} //namespace BALL

<commit_msg>Remove some debug info in glRenderWindow<commit_after>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//

#include <GL/glew.h>

#include <BALL/VIEW/RENDERING/glRenderWindow.h>
#include <BALL/VIEW/WIDGETS/scene.h>
#include <BALL/COMMON/logStream.h>
#include <BALL/VIEW/KERNEL/common.h>

#include <QtCore/QEvent>
#include <QtGui/QPaintEvent>

//#define USE_GLPAINTPIXELS
#undef USE_GLPAINTPIXELS

namespace BALL
{
	namespace VIEW
	{
	  QGLFormat GLRenderWindow::gl_format_(
				QGL::DepthBuffer 		 |
#ifndef BALL_OS_DARWIN
				QGL::StereoBuffers 	 |
#endif
				QGL::DoubleBuffer 	 |
				QGL::DirectRendering |
				QGL::SampleBuffers   |
				QGL::StencilBuffer);

		GLRenderWindow::GLRenderWindow()
			: QGLWidget(gl_format_),
			  stereo_delta_(0.),
			  m_screenTexID(0),
			  FB_TEXTURE_TARGET(GL_TEXTURE_2D),
			  FB_TEXTURE_FORMAT(GL_RGB),
			  FB_INTERNAL_TEXTURE_FORMAT(GL_RGB),
			  FB_TEXTURE_DATATYPE(GL_FLOAT),
			  ignore_events_(false),
			  down_sampling_factor_(1.)
		{
			// we will swap buffers manually in the scene for synchronization
			setAutoBufferSwap(false);
			setAutoFillBackground(false);
		}

		GLRenderWindow::GLRenderWindow(QWidget* parent_widget, const char* /*name*/, Qt::WFlags w_flags)
			: QGLWidget(gl_format_, parent_widget, (QGLWidget*)0, w_flags),
			  stereo_delta_(0.),
			  m_screenTexID(0),
			  FB_TEXTURE_TARGET(GL_TEXTURE_2D),
			  FB_TEXTURE_FORMAT(GL_RGB),
			  FB_INTERNAL_TEXTURE_FORMAT(GL_RGB),
			  FB_TEXTURE_DATATYPE(GL_FLOAT),
			  ignore_events_(false),
			  down_sampling_factor_(1)
		{
			if (!QGLWidget::isValid())
			{
				Log.error() << "QGLWidget is not valid in Scene!" << std::endl;
			}
			// we will swap buffers manually in the scene for synchronization
			setAutoBufferSwap(false);
			setAutoFillBackground(false);
		}

		GLRenderWindow::GLRenderWindow(const GLRenderWindow& window, QWidget* parent_widget, const char* /*name*/, Qt::WFlags w_flags)
			: QGLWidget(gl_format_, parent_widget, reinterpret_cast<QGLWidget const*>(&window), w_flags),
			  stereo_delta_(0.),
			  m_screenTexID(0),
			  FB_TEXTURE_TARGET(GL_TEXTURE_2D),
			  FB_TEXTURE_FORMAT(GL_RGB),
			  FB_INTERNAL_TEXTURE_FORMAT(GL_RGB),
			  FB_TEXTURE_DATATYPE(GL_FLOAT),
			  ignore_events_(false),
			  down_sampling_factor_(1.)
		{
			// we will swap buffers manually in the scene for synchronization
			setAutoBufferSwap(false);
			setAutoFillBackground(false);
		}

		GLRenderWindow::~GLRenderWindow()
		{
			deleteTexture();
		}

		bool GLRenderWindow::init()
		{
			checkGL();

			// TODO: is this necessary?
			if (!format().rgba())
				Log.error() << "No RGBA mode for OpenGl available." << std::endl;

			RenderWindow::init();
			bool result = false;

			FB_TEXTURE_TARGET = GL_TEXTURE_2D;

			if(m_fmt.getPixelFormat() == PixelFormat::RGBF_96)
			{
				FB_INTERNAL_TEXTURE_FORMAT = GL_RGB;
				FB_TEXTURE_FORMAT = GL_RGB;
				FB_TEXTURE_DATATYPE = GL_FLOAT;
				result = true;
			}
			else
			{
				if(m_fmt.getPixelFormat() == PixelFormat::RGBA_32)
				{
					FB_INTERNAL_TEXTURE_FORMAT = GL_RGBA;
					FB_TEXTURE_FORMAT = GL_RGBA;
					FB_TEXTURE_DATATYPE = GL_UNSIGNED_BYTE;
				}
				result = true;
			}
			return result;
		}

		bool GLRenderWindow::resize(const unsigned int width, const unsigned int height)
		{
			if(!RenderWindow::resize((int)ceil(width/down_sampling_factor_), (int)ceil(height/down_sampling_factor_)))
			{
				return false;
			}
			createTexture((int)ceil(width/down_sampling_factor_), (int)ceil(height/down_sampling_factor_));

			QGLWidget::resize(width, height);

			return true;
		}

		void GLRenderWindow::refresh()
		{
			RenderWindow::refresh();

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			glPushAttrib(GL_TEXTURE_BIT);
			glPushAttrib(GL_DEPTH_BUFFER_BIT);

#ifdef USE_GLPAINTPIXELS
			glDrawPixels(m_fmt.getWidth(), m_fmt.getHeight(), FB_TEXTURE_FORMAT, FB_TEXTURE_DATATYPE, m_pixels.get());
#else
			glBindTexture(FB_TEXTURE_TARGET, m_screenTexID);
			glTexSubImage2D(FB_TEXTURE_TARGET, 0, 0, 0, m_fmt.getWidth(), m_fmt.getHeight(),
					FB_TEXTURE_FORMAT, FB_TEXTURE_DATATYPE, m_pixels.get());

			glEnable(FB_TEXTURE_TARGET);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#endif

			glDisable(GL_DEPTH_TEST);
			glMatrixMode(GL_PROJECTION);
			glPushMatrix();

			glLoadIdentity();

			glPushAttrib(GL_VIEWPORT_BIT);
#ifndef USE_GLPAINTPIXELS
			glViewport(0, 0, down_sampling_factor_*m_fmt.getWidth(), down_sampling_factor_*m_fmt.getHeight());
			float aspectRatio = static_cast<float>(m_fmt.getWidth()) / m_fmt.getHeight();
			glOrtho(-aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f);

			float origWidth = static_cast<float>(m_fmt.getWidth());
			float newWidth = (origWidth + fabs(stereo_delta_));
			float newRatio = origWidth / newWidth;
			float deltaRatio = 1. - newRatio;

			glBegin(GL_QUADS);

			if (stereo_delta_ <= 0.)
			{
				glTexCoord2f(0.0f+deltaRatio, 0.0f);
				glVertex2f(-aspectRatio, -1.0f);

				glTexCoord2f(1.0f, 0.0f );
				glVertex2f(aspectRatio, -1.0f);

				glTexCoord2f(1.0f, 1.0f );
				glVertex2f(aspectRatio, 1.0f);

				glTexCoord2f(0.0f+deltaRatio, 1.0f);
				glVertex2f(-aspectRatio, 1.0f);
			}
			else
			{
				glTexCoord2f(0.0f, 0.0f);
				glVertex2f(-aspectRatio, -1.0f);

				glTexCoord2f(1.0f-deltaRatio, 0.0f );
				glVertex2f(aspectRatio, -1.0f);

				glTexCoord2f(1.0f-deltaRatio, 1.0f );
				glVertex2f(aspectRatio, 1.0f);

				glTexCoord2f(0.0f, 1.0f);
				glVertex2f(-aspectRatio, 1.0f);
			}

			glEnd();
#endif
			glPopAttrib();

			glPopMatrix();
			glMatrixMode(GL_MODELVIEW);

			glPopAttrib();
			glPopAttrib();
		}

		void GLRenderWindow::renderText(int x, int y, const String& text, const ColorRGBA& color, Size size)
		{
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			glViewport(0, 0, m_fmt.getWidth(), m_fmt.getHeight());

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			QFont font;
			font.setPixelSize(size);
			font.setBold(true);

			glDisable(GL_LIGHTING);
			glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
			QGLWidget::renderText(x, y, text.c_str(), font);
			glEnable(GL_LIGHTING);
		}

		void GLRenderWindow::renderText(float x, float y, float z, const String& text, const ColorRGBA& color, Size size)
		{
			// TEST!
			return;
			QFont font;
			font.setPixelSize(size);
			font.setBold(true);

			glDisable(GL_LIGHTING);
			glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
			QGLWidget::renderText(x, y, z, text.c_str(), font);
			glEnable(GL_LIGHTING);
		}

		void GLRenderWindow::createTexture(const unsigned int width, const unsigned int height)
		{
			if(m_screenTexID != 0)
			{
				deleteTexture();
			}

			glGenTextures(1, &m_screenTexID);

			glBindTexture(FB_TEXTURE_TARGET, m_screenTexID);

			glTexParameteri(FB_TEXTURE_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(FB_TEXTURE_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			glTexImage2D(FB_TEXTURE_TARGET, 0, FB_INTERNAL_TEXTURE_FORMAT, width, height, 0, FB_TEXTURE_FORMAT, FB_TEXTURE_DATATYPE, NULL);

			glBindTexture(FB_TEXTURE_TARGET, 0);
		}

		void GLRenderWindow::deleteTexture()
		{
			glDeleteTextures(1, &m_screenTexID);
			m_screenTexID = 0;
		}

		bool GLRenderWindow::errorInGL(GLenum& error)
		{
				error = glGetError();
				return (error != GL_NO_ERROR);
		}

		String GLRenderWindow::getGLErrorString(GLenum error)
		{
				String result;
				switch(error)
				{
				case GL_INVALID_ENUM:
						result = "Invalid enumeration value";
						break;
				case GL_INVALID_VALUE:
						result = "Numeric argument out of range";
						break;
				case GL_INVALID_OPERATION:
						result = "Operation illegal in current state";
						break;
				case GL_STACK_OVERFLOW:
						result = "Command would cause stack overflow";
						break;
				case GL_STACK_UNDERFLOW:
						result = "Command would cause stack underflow";
						break;
				case GL_OUT_OF_MEMORY:
						result = "Not enough memory left to execute command";
						break;
				case GL_TABLE_TOO_LARGE:
						result = "The specified table is too large";
						break;
				default:
						result = "Uknown OpenGL error";
				}
				return result;
		}

		void GLRenderWindow::checkGL()
		{
				GLenum err;
				if(errorInGL(err))
				{
					BALL::Log.error() << "Error in OpenGL: " << getGLErrorString(err) << std::endl;
				}
		}

		void GLRenderWindow::customEvent(QEvent* evt)
		{
			switch(static_cast<EventsIDs>(evt->type())) {
				case RENDER_TO_BUFFER_FINISHED_EVENT:
					refresh();
					swapBuffers();
					break;
			}
		}

		void GLRenderWindow::paintEvent(QPaintEvent* e)
		{
			if (!ignore_events_)
			{
				QGLWidget::paintEvent(e);
			}
		}

		void GLRenderWindow::lockGLContext()
		{
			contex_mutex_.lock();
			makeCurrent();
		}

		void GLRenderWindow::unlockGLContext()
		{
			doneCurrent();
			contex_mutex_.unlock();
		}

		void GLRenderWindow::setupStereo(float eye_separation, float focal_length)
		{
				float aperture = 60.;
				float width = static_cast<float>(m_fmt.getWidth());

				//formula according to Paul Bourke
				//http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/stereographics/stereorender/
				stereo_delta_ = (fabs(eye_separation) * width) / (focal_length * tan(Angle(aperture, false).toRadian()));
				std::cout << stereo_delta_ << std::endl;
		}
	} // namespace VIEW
} //namespace BALL

<|endoftext|>


<commit_before><commit_msg>vcl: vcldemo now shows Arabic text<commit_after><|endoftext|>


<commit_before>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

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

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

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <vector>
#include <iostream>

#include <boost/algorithm/string/replace.hpp>
#include <iomanip>

#include <bh_instruction.hpp>
#include <bh_component.hpp>
#include <jitk/symbol_table.hpp>

#include "engine_cuda.hpp"

using namespace std;
namespace fs = boost::filesystem;

namespace bohrium {

EngineCUDA::EngineCUDA(const ConfigParser &config, jitk::Statistics &stat) :
        EngineGPU(config, stat),
        work_group_size_1dx(config.defaultGet<int>("work_group_size_1dx", 128)),
        work_group_size_2dx(config.defaultGet<int>("work_group_size_2dx", 32)),
        work_group_size_2dy(config.defaultGet<int>("work_group_size_2dy", 4)),
        work_group_size_3dx(config.defaultGet<int>("work_group_size_3dx", 32)),
        work_group_size_3dy(config.defaultGet<int>("work_group_size_3dy", 2)),
        work_group_size_3dz(config.defaultGet<int>("work_group_size_3dz", 2)) {
    int deviceCount = 0;
    CUresult err = cuInit(0);

    if (err == CUDA_SUCCESS)
        check_cuda_errors(cuDeviceGetCount(&deviceCount));

    if (deviceCount == 0) {
        throw runtime_error("Error: no devices supporting CUDA");
    }

    // get first CUDA device
    check_cuda_errors(cuDeviceGet(&device, 0));

    err = cuCtxCreate(&context, 0, device);
    if (err != CUDA_SUCCESS) {
        cuCtxDetach(context);
        throw runtime_error("Error initializing the CUDA context.");
    }

    // Let's make sure that the directories exist
    jitk::create_directories(tmp_src_dir);
    jitk::create_directories(tmp_bin_dir);
    if (not cache_bin_dir.empty()) {
        jitk::create_directories(cache_bin_dir);
    }

    // Write the compilation hash
    compilation_hash = util::hash(info());

    // Get the compiler command and replace {MAJOR} and {MINOR} with the SM versions
    string compiler_cmd = config.get<string>("compiler_cmd");

    int major = 0, minor = 0;
    check_cuda_errors(cuDeviceComputeCapability(&major, &minor, device));
    boost::replace_all(compiler_cmd, "{MAJOR}", std::to_string(major));
    boost::replace_all(compiler_cmd, "{MINOR}", std::to_string(minor));

    // Init the compiler
    compiler = jitk::Compiler(compiler_cmd, verbose, config.file_dir.string());

    // Initiate cache limits
    size_t gpu_mem;
    check_cuda_errors(cuDeviceTotalMem(&gpu_mem, device));
    malloc_cache_limit_in_percent = config.defaultGet<int64_t>("malloc_cache_limit", 90);
    if (malloc_cache_limit_in_percent < 0 or malloc_cache_limit_in_percent > 100) {
        throw std::runtime_error("config: `malloc_cache_limit` must be between 0 and 100");
    }
    malloc_cache_limit_in_bytes = static_cast<int64_t>(std::floor(gpu_mem * (malloc_cache_limit_in_percent/100.0)));
    malloc_cache.setLimit(static_cast<uint64_t>(malloc_cache_limit_in_bytes));
}

EngineCUDA::~EngineCUDA() {

    // Move JIT kernels to the cache dir
    if (not cache_bin_dir.empty()) {
        try {
            for (const auto &kernel: _functions) {
                const fs::path src = tmp_bin_dir / jitk::hash_filename(compilation_hash, kernel.first, ".cubin");
                if (fs::exists(src)) {
                    const fs::path dst = cache_bin_dir / jitk::hash_filename(compilation_hash, kernel.first, ".cubin");
                    if (not fs::exists(dst)) {
                        fs::copy_file(src, dst);
                    }
                }
            }
        } catch (const boost::filesystem::filesystem_error &e) {
            cout << "Warning: couldn't write CUDA kernels to disk to " << cache_bin_dir
                 << ". " << e.what() << endl;
        }
    }

    // File clean up
    if (not verbose) {
        fs::remove_all(tmp_src_dir);
    }

    if (cache_file_max != -1 and not cache_bin_dir.empty()) {
        util::remove_old_files(cache_bin_dir, cache_file_max);
    }

    // We empty the malloc cache before detaching the context
    malloc_cache.shrinkToFit(0);
    cuCtxDetach(context);
}

pair<tuple<uint32_t, uint32_t, uint32_t>, tuple<uint32_t, uint32_t, uint32_t> >
EngineCUDA::NDRanges(const vector<uint64_t> &thread_stack) const {
    const auto &b = thread_stack;
    switch (b.size()) {
        case 1: {
            const auto gsize_and_lsize = jitk::work_ranges(work_group_size_1dx, b[0]);
            return make_pair(make_tuple(gsize_and_lsize.first, 1, 1), make_tuple(gsize_and_lsize.second, 1, 1));
        }
        case 2: {
            const auto gsize_and_lsize_x = jitk::work_ranges(work_group_size_2dx, b[0]);
            const auto gsize_and_lsize_y = jitk::work_ranges(work_group_size_2dy, b[1]);
            return make_pair(make_tuple(gsize_and_lsize_x.first, gsize_and_lsize_y.first, 1),
                             make_tuple(gsize_and_lsize_x.second, gsize_and_lsize_y.second, 1));
        }
        case 3: {
            const auto gsize_and_lsize_x = jitk::work_ranges(work_group_size_3dx, b[0]);
            const auto gsize_and_lsize_y = jitk::work_ranges(work_group_size_3dy, b[1]);
            const auto gsize_and_lsize_z = jitk::work_ranges(work_group_size_3dz, b[2]);
            return make_pair(make_tuple(gsize_and_lsize_x.first, gsize_and_lsize_y.first, gsize_and_lsize_z.first),
                             make_tuple(gsize_and_lsize_x.second, gsize_and_lsize_y.second, gsize_and_lsize_z.second));
        }
        default:
            throw runtime_error("NDRanges: maximum of three dimensions!");
    }
}

CUfunction EngineCUDA::getFunction(const string &source, const std::string &func_name) {
    uint64_t hash = util::hash(source);
    ++stat.kernel_cache_lookups;

    // Do we have the program already?
    if (_functions.find(hash) != _functions.end()) {
        return _functions.at(hash);
    }

    fs::path binfile = cache_bin_dir / jitk::hash_filename(compilation_hash, hash, ".cubin");

    // If the binary file of the kernel doesn't exist we create it
    if (verbose or cache_bin_dir.empty() or not fs::exists(binfile)) {
        ++stat.kernel_cache_misses;

        // We create the binary file in the tmp dir
        binfile = tmp_bin_dir / jitk::hash_filename(compilation_hash, hash, ".cubin");

        // Write the source file and compile it (reading from disk)
        // TODO: make nvcc read directly from stdin
        {
            std::string kernel_filename = jitk::hash_filename(compilation_hash, hash, ".cu");
            fs::path srcfile = jitk::write_source2file(source, tmp_src_dir,
                                                       kernel_filename, verbose);
            compiler.compile(binfile.string(), srcfile.string());
        }
        /* else {
            // Pipe the source directly into the compiler thus no source file is written
            compiler.compile(binfile.string(), source.c_str(), source.size());
        }
       */
    }

    CUmodule module;
    CUresult err = cuModuleLoad(&module, binfile.string().c_str());
    if (err != CUDA_SUCCESS) {
        const char *err_name, *err_desc;
        cuGetErrorName(err, &err_name);
        cuGetErrorString(err, &err_desc);
        cout << "Error loading the module \"" << binfile.string()
             << "\", " << err_name << ": \"" << err_desc << "\"." << endl;
        cuCtxDetach(context);
        throw runtime_error("cuModuleLoad() failed");
    }

    CUfunction program;
    err = cuModuleGetFunction(&program, module, func_name.c_str());
    if (err != CUDA_SUCCESS) {
        const char *err_name, *err_desc;
        cuGetErrorName(err, &err_name);
        cuGetErrorString(err, &err_desc);
        cout << "Error getting kernel function 'execute' \"" << binfile.string()
             << "\", " << err_name << ": \"" << err_desc << "\"." << endl;
        throw runtime_error("cuModuleGetFunction() failed");
    }
    _functions[hash] = program;
    return program;
}

void EngineCUDA::writeKernel(const jitk::Block &block,
                             const jitk::SymbolTable &symbols,
                             const std::vector<uint64_t> &thread_stack,
                             uint64_t codegen_hash,
                             std::stringstream &ss) {
    // Write the need includes
    ss << "#include <kernel_dependencies/complex_cuda.h>\n";
    ss << "#include <kernel_dependencies/integer_operations.h>\n";
    if (symbols.useRandom()) { // Write the random function
        ss << "#include <kernel_dependencies/random123_cuda.h>\n";
    }
    ss << "\n";

    // Write the header of the execute function
    ss << "extern \"C\" __global__ void execute_" << codegen_hash;
    writeKernelFunctionArguments(symbols, ss, nullptr);
    ss << " {\n";

    // Write the IDs of the threaded blocks
    if (not thread_stack.empty()) {
        util::spaces(ss, 4);
        ss << "// The IDs of the threaded blocks: \n";
        for (unsigned int i = 0; i < thread_stack.size(); ++i) {
            util::spaces(ss, 4);
            ss << "const " << writeType(bh_type::INT64) << " i" << i << " = " << writeThreadId(i) << "; "
               << "if (i" << i << " >= " << thread_stack[i] << ") { return; } // Prevent overflow\n";
        }
        ss << "\n";
    }
    writeLoopBlock(symbols, nullptr, block.getLoop(), thread_stack, true, ss);
    ss << "}\n\n";
}

void EngineCUDA::execute(const jitk::SymbolTable &symbols,
                         const std::string &source,
                         uint64_t codegen_hash,
                         const vector<uint64_t> &thread_stack,
                         const vector<const bh_instruction *> &constants) {
    uint64_t hash = util::hash(source);
    std::string source_filename = jitk::hash_filename(compilation_hash, hash, ".cu");

    auto tcompile = chrono::steady_clock::now();
    string func_name;
    {
        stringstream t;
        t << "execute_" << codegen_hash;
        func_name = t.str();
    }
    CUfunction program = getFunction(source, func_name);
    stat.time_compile += chrono::steady_clock::now() - tcompile;

    // Let's execute the CUDA kernel
    vector<void *> args;

    for (bh_base *base: symbols.getParams()) { // NB: the iteration order matters!
        args.push_back(getBuffer(base));
    }

    for (const bh_view *view: symbols.offsetStrideViews()) {
        args.push_back((void *) &view->start);
        for (int j = 0; j < view->ndim; ++j) {
            args.push_back((void *) &view->stride[j]);
        }
    }

    auto exec_start = chrono::steady_clock::now();

    tuple<uint32_t, uint32_t, uint32_t> blocks, threads;
    tie(blocks, threads) = NDRanges(thread_stack);

    check_cuda_errors(cuLaunchKernel(program,
                                     get<0>(blocks), get<1>(blocks), get<2>(blocks),  // NxNxN blocks
                                     get<0>(threads), get<1>(threads), get<2>(threads),  // NxNxN threads
                                     0, 0, &args[0], 0));
    check_cuda_errors(cuCtxSynchronize());

    auto texec = chrono::steady_clock::now() - exec_start;
    stat.time_exec += texec;
    stat.time_per_kernel[source_filename].register_exec_time(texec);
}

void EngineCUDA::setConstructorFlag(std::vector<bh_instruction *> &instr_list) {
    jitk::util_set_constructor_flag(instr_list, buffers);
}

std::string EngineCUDA::info() const {
    char device_name[1000];
    cuDeviceGetName(device_name, 1000, device);
    int major = 0, minor = 0;
    check_cuda_errors(cuDeviceComputeCapability(&major, &minor, device));
    size_t totalGlobalMem;
    check_cuda_errors(cuDeviceTotalMem(&totalGlobalMem, device));

    stringstream ss;
    ss << std::boolalpha; // Printing true/false instead of 1/0
    ss << "----"                                                                               << "\n";
    ss << "CUDA:"                                                                            << "\n";
    ss << "  Device: " << device_name << " (SM " << major << "." << minor << " compute capability)\"\n";
    ss << "  Memory: " << totalGlobalMem / 1024 / 1024 << " MB\n";
    ss << "  Malloc cache limit: " << malloc_cache_limit_in_bytes / 1024 / 1024
       << " MB (" << malloc_cache_limit_in_percent << "%)\n";
    ss << "  JIT Command: " << compiler.cmd_template << "\n";
    ss << "  Cache dir: " << config.defaultGet<string>("cache_dir", "")  << "\n";
    ss << "  Temp dir: " << jitk::get_tmp_path(config)  << "\n";

    ss << "  Codegen flags:\n";
    ss << "    Index-as-var: " << config.defaultGet<bool>("index_as_var", true)  << "\n";
    ss << "    Strides-as-var: " << config.defaultGet<bool>("strides_as_var", true)  << "\n";
    ss << "    const-as-var: " << config.defaultGet<bool>("const_as_var", true)  << "\n";
    return ss.str();
}

} // bohrium
<commit_msg>cuda: fixing the compilation hash<commit_after>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

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

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

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <vector>
#include <iostream>

#include <boost/algorithm/string/replace.hpp>
#include <iomanip>

#include <bh_instruction.hpp>
#include <bh_component.hpp>
#include <jitk/symbol_table.hpp>

#include "engine_cuda.hpp"

using namespace std;
namespace fs = boost::filesystem;

namespace bohrium {

EngineCUDA::EngineCUDA(const ConfigParser &config, jitk::Statistics &stat) :
        EngineGPU(config, stat),
        work_group_size_1dx(config.defaultGet<int>("work_group_size_1dx", 128)),
        work_group_size_2dx(config.defaultGet<int>("work_group_size_2dx", 32)),
        work_group_size_2dy(config.defaultGet<int>("work_group_size_2dy", 4)),
        work_group_size_3dx(config.defaultGet<int>("work_group_size_3dx", 32)),
        work_group_size_3dy(config.defaultGet<int>("work_group_size_3dy", 2)),
        work_group_size_3dz(config.defaultGet<int>("work_group_size_3dz", 2)) {
    int deviceCount = 0;
    CUresult err = cuInit(0);

    if (err == CUDA_SUCCESS)
        check_cuda_errors(cuDeviceGetCount(&deviceCount));

    if (deviceCount == 0) {
        throw runtime_error("Error: no devices supporting CUDA");
    }

    // get first CUDA device
    check_cuda_errors(cuDeviceGet(&device, 0));

    err = cuCtxCreate(&context, 0, device);
    if (err != CUDA_SUCCESS) {
        cuCtxDetach(context);
        throw runtime_error("Error initializing the CUDA context.");
    }

    // Let's make sure that the directories exist
    jitk::create_directories(tmp_src_dir);
    jitk::create_directories(tmp_bin_dir);
    if (not cache_bin_dir.empty()) {
        jitk::create_directories(cache_bin_dir);
    }

    // Get the compiler command and replace {MAJOR} and {MINOR} with the SM versions
    string compiler_cmd = config.get<string>("compiler_cmd");

    int major = 0, minor = 0;
    check_cuda_errors(cuDeviceComputeCapability(&major, &minor, device));
    boost::replace_all(compiler_cmd, "{MAJOR}", std::to_string(major));
    boost::replace_all(compiler_cmd, "{MINOR}", std::to_string(minor));

    // Initiate the compiler
    compiler = jitk::Compiler(compiler_cmd, verbose, config.file_dir.string());

    // Write the compilation hash
    {
        char device_name[1000];
        cuDeviceGetName(device_name, 1000, device);
        stringstream ss;
        ss << compiler_cmd << device_name << major << minor;
        compilation_hash = util::hash(ss.str());
    }

    // Initiate cache limits
    size_t gpu_mem;
    check_cuda_errors(cuDeviceTotalMem(&gpu_mem, device));
    malloc_cache_limit_in_percent = config.defaultGet<int64_t>("malloc_cache_limit", 90);
    if (malloc_cache_limit_in_percent < 0 or malloc_cache_limit_in_percent > 100) {
        throw std::runtime_error("config: `malloc_cache_limit` must be between 0 and 100");
    }
    malloc_cache_limit_in_bytes = static_cast<int64_t>(std::floor(gpu_mem * (malloc_cache_limit_in_percent/100.0)));
    malloc_cache.setLimit(static_cast<uint64_t>(malloc_cache_limit_in_bytes));
}

EngineCUDA::~EngineCUDA() {

    // Move JIT kernels to the cache dir
    if (not cache_bin_dir.empty()) {
        try {
            for (const auto &kernel: _functions) {
                const fs::path src = tmp_bin_dir / jitk::hash_filename(compilation_hash, kernel.first, ".cubin");
                if (fs::exists(src)) {
                    const fs::path dst = cache_bin_dir / jitk::hash_filename(compilation_hash, kernel.first, ".cubin");
                    if (not fs::exists(dst)) {
                        fs::copy_file(src, dst);
                    }
                }
            }
        } catch (const boost::filesystem::filesystem_error &e) {
            cout << "Warning: couldn't write CUDA kernels to disk to " << cache_bin_dir
                 << ". " << e.what() << endl;
        }
    }

    // File clean up
    if (not verbose) {
        fs::remove_all(tmp_src_dir);
    }

    if (cache_file_max != -1 and not cache_bin_dir.empty()) {
        util::remove_old_files(cache_bin_dir, cache_file_max);
    }

    // We empty the malloc cache before detaching the context
    malloc_cache.shrinkToFit(0);
    cuCtxDetach(context);
}

pair<tuple<uint32_t, uint32_t, uint32_t>, tuple<uint32_t, uint32_t, uint32_t> >
EngineCUDA::NDRanges(const vector<uint64_t> &thread_stack) const {
    const auto &b = thread_stack;
    switch (b.size()) {
        case 1: {
            const auto gsize_and_lsize = jitk::work_ranges(work_group_size_1dx, b[0]);
            return make_pair(make_tuple(gsize_and_lsize.first, 1, 1), make_tuple(gsize_and_lsize.second, 1, 1));
        }
        case 2: {
            const auto gsize_and_lsize_x = jitk::work_ranges(work_group_size_2dx, b[0]);
            const auto gsize_and_lsize_y = jitk::work_ranges(work_group_size_2dy, b[1]);
            return make_pair(make_tuple(gsize_and_lsize_x.first, gsize_and_lsize_y.first, 1),
                             make_tuple(gsize_and_lsize_x.second, gsize_and_lsize_y.second, 1));
        }
        case 3: {
            const auto gsize_and_lsize_x = jitk::work_ranges(work_group_size_3dx, b[0]);
            const auto gsize_and_lsize_y = jitk::work_ranges(work_group_size_3dy, b[1]);
            const auto gsize_and_lsize_z = jitk::work_ranges(work_group_size_3dz, b[2]);
            return make_pair(make_tuple(gsize_and_lsize_x.first, gsize_and_lsize_y.first, gsize_and_lsize_z.first),
                             make_tuple(gsize_and_lsize_x.second, gsize_and_lsize_y.second, gsize_and_lsize_z.second));
        }
        default:
            throw runtime_error("NDRanges: maximum of three dimensions!");
    }
}

CUfunction EngineCUDA::getFunction(const string &source, const std::string &func_name) {
    uint64_t hash = util::hash(source);
    ++stat.kernel_cache_lookups;

    // Do we have the program already?
    if (_functions.find(hash) != _functions.end()) {
        return _functions.at(hash);
    }

    fs::path binfile = cache_bin_dir / jitk::hash_filename(compilation_hash, hash, ".cubin");

    // If the binary file of the kernel doesn't exist we create it
    if (verbose or cache_bin_dir.empty() or not fs::exists(binfile)) {
        ++stat.kernel_cache_misses;

        // We create the binary file in the tmp dir
        binfile = tmp_bin_dir / jitk::hash_filename(compilation_hash, hash, ".cubin");

        // Write the source file and compile it (reading from disk)
        // TODO: make nvcc read directly from stdin
        {
            std::string kernel_filename = jitk::hash_filename(compilation_hash, hash, ".cu");
            fs::path srcfile = jitk::write_source2file(source, tmp_src_dir,
                                                       kernel_filename, verbose);
            compiler.compile(binfile.string(), srcfile.string());
        }
        /* else {
            // Pipe the source directly into the compiler thus no source file is written
            compiler.compile(binfile.string(), source.c_str(), source.size());
        }
       */
    }

    CUmodule module;
    CUresult err = cuModuleLoad(&module, binfile.string().c_str());
    if (err != CUDA_SUCCESS) {
        const char *err_name, *err_desc;
        cuGetErrorName(err, &err_name);
        cuGetErrorString(err, &err_desc);
        cout << "Error loading the module \"" << binfile.string()
             << "\", " << err_name << ": \"" << err_desc << "\"." << endl;
        cuCtxDetach(context);
        throw runtime_error("cuModuleLoad() failed");
    }

    CUfunction program;
    err = cuModuleGetFunction(&program, module, func_name.c_str());
    if (err != CUDA_SUCCESS) {
        const char *err_name, *err_desc;
        cuGetErrorName(err, &err_name);
        cuGetErrorString(err, &err_desc);
        cout << "Error getting kernel function 'execute' \"" << binfile.string()
             << "\", " << err_name << ": \"" << err_desc << "\"." << endl;
        throw runtime_error("cuModuleGetFunction() failed");
    }
    _functions[hash] = program;
    return program;
}

void EngineCUDA::writeKernel(const jitk::Block &block,
                             const jitk::SymbolTable &symbols,
                             const std::vector<uint64_t> &thread_stack,
                             uint64_t codegen_hash,
                             std::stringstream &ss) {
    // Write the need includes
    ss << "#include <kernel_dependencies/complex_cuda.h>\n";
    ss << "#include <kernel_dependencies/integer_operations.h>\n";
    if (symbols.useRandom()) { // Write the random function
        ss << "#include <kernel_dependencies/random123_cuda.h>\n";
    }
    ss << "\n";

    // Write the header of the execute function
    ss << "extern \"C\" __global__ void execute_" << codegen_hash;
    writeKernelFunctionArguments(symbols, ss, nullptr);
    ss << " {\n";

    // Write the IDs of the threaded blocks
    if (not thread_stack.empty()) {
        util::spaces(ss, 4);
        ss << "// The IDs of the threaded blocks: \n";
        for (unsigned int i = 0; i < thread_stack.size(); ++i) {
            util::spaces(ss, 4);
            ss << "const " << writeType(bh_type::INT64) << " i" << i << " = " << writeThreadId(i) << "; "
               << "if (i" << i << " >= " << thread_stack[i] << ") { return; } // Prevent overflow\n";
        }
        ss << "\n";
    }
    writeLoopBlock(symbols, nullptr, block.getLoop(), thread_stack, true, ss);
    ss << "}\n\n";
}

void EngineCUDA::execute(const jitk::SymbolTable &symbols,
                         const std::string &source,
                         uint64_t codegen_hash,
                         const vector<uint64_t> &thread_stack,
                         const vector<const bh_instruction *> &constants) {
    uint64_t hash = util::hash(source);
    std::string source_filename = jitk::hash_filename(compilation_hash, hash, ".cu");

    auto tcompile = chrono::steady_clock::now();
    string func_name;
    {
        stringstream t;
        t << "execute_" << codegen_hash;
        func_name = t.str();
    }
    CUfunction program = getFunction(source, func_name);
    stat.time_compile += chrono::steady_clock::now() - tcompile;

    // Let's execute the CUDA kernel
    vector<void *> args;

    for (bh_base *base: symbols.getParams()) { // NB: the iteration order matters!
        args.push_back(getBuffer(base));
    }

    for (const bh_view *view: symbols.offsetStrideViews()) {
        args.push_back((void *) &view->start);
        for (int j = 0; j < view->ndim; ++j) {
            args.push_back((void *) &view->stride[j]);
        }
    }

    auto exec_start = chrono::steady_clock::now();

    tuple<uint32_t, uint32_t, uint32_t> blocks, threads;
    tie(blocks, threads) = NDRanges(thread_stack);

    check_cuda_errors(cuLaunchKernel(program,
                                     get<0>(blocks), get<1>(blocks), get<2>(blocks),  // NxNxN blocks
                                     get<0>(threads), get<1>(threads), get<2>(threads),  // NxNxN threads
                                     0, 0, &args[0], 0));
    check_cuda_errors(cuCtxSynchronize());

    auto texec = chrono::steady_clock::now() - exec_start;
    stat.time_exec += texec;
    stat.time_per_kernel[source_filename].register_exec_time(texec);
}

void EngineCUDA::setConstructorFlag(std::vector<bh_instruction *> &instr_list) {
    jitk::util_set_constructor_flag(instr_list, buffers);
}

std::string EngineCUDA::info() const {
    char device_name[1000];
    cuDeviceGetName(device_name, 1000, device);
    int major = 0, minor = 0;
    check_cuda_errors(cuDeviceComputeCapability(&major, &minor, device));
    size_t totalGlobalMem;
    check_cuda_errors(cuDeviceTotalMem(&totalGlobalMem, device));

    stringstream ss;
    ss << std::boolalpha; // Printing true/false instead of 1/0
    ss << "----"                                                                               << "\n";
    ss << "CUDA:"                                                                            << "\n";
    ss << "  Device: " << device_name << " (SM " << major << "." << minor << " compute capability)\"\n";
    ss << "  Memory: " << totalGlobalMem / 1024 / 1024 << " MB\n";
    ss << "  Malloc cache limit: " << malloc_cache_limit_in_bytes / 1024 / 1024
       << " MB (" << malloc_cache_limit_in_percent << "%)\n";
    ss << "  JIT Command: " << compiler.cmd_template << "\n";
    ss << "  Cache dir: " << config.defaultGet<string>("cache_dir", "")  << "\n";
    ss << "  Temp dir: " << jitk::get_tmp_path(config)  << "\n";

    ss << "  Codegen flags:\n";
    ss << "    Index-as-var: " << config.defaultGet<bool>("index_as_var", true)  << "\n";
    ss << "    Strides-as-var: " << config.defaultGet<bool>("strides_as_var", true)  << "\n";
    ss << "    const-as-var: " << config.defaultGet<bool>("const_as_var", true)  << "\n";
    return ss.str();
}

} // bohrium
<|endoftext|>


<commit_before>#ifndef TUT_ASSERT_H_GUARD
#define TUT_ASSERT_H_GUARD

#include "tut_exception.hpp"
#include <limits>
#include <iomanip>

#if defined(TUT_USE_POSIX)
#include <errno.h>
#include <cstring>
#endif

namespace tut
{

namespace
{

/**
 * Tests provided condition.
 * Throws if false.
 */
void ensure(bool cond)
{
    if (!cond)
    {
        // TODO: default ctor?
        throw failure("");
    }
}

/**
 * Tests provided condition.
 * Throws if true.
 */
void ensure_not(bool cond)
{
    ensure(!cond);
}

/**
 * Tests provided condition.
 * Throws if false.
 */
template <typename M>
void ensure(const M& msg, bool cond)
{
    if (!cond)
    {
        throw failure(msg);
    }
}

/**
 * Tests provided condition.
 * Throws if true.
 */
template <typename M>
void ensure_not(const M& msg, bool cond)
{
    ensure(msg, !cond);
}

/**
 * Tests two objects for being equal.
 * Throws if false.
 *
 * NB: both T and Q must have operator << defined somewhere, or
 * client code will not compile at all!
 */
template <typename M, typename LHS, typename RHS>
void ensure_equals(const M& msg, const LHS& actual, const RHS& expected)
{
    if (expected != actual)
    {
        std::stringstream ss;
        ss << msg << ":"
           << " expected '"
           << expected
           << "' actual '"
           << actual
           << '\'';
        throw failure(ss.str());
    }
}

template <typename LHS, typename RHS>
void ensure_equals(const LHS& actual, const RHS& expected)
{
    ensure_equals("Values are not equal", actual, expected);
}

template<typename M>
void ensure_equals(const M& msg, const double& actual, const double& expected,
                   const double& epsilon = std::numeric_limits<double>::epsilon())
{
    const double diff = actual - expected;

    if ( !((diff <= epsilon) && (diff >= -epsilon )) )
    {
        std::stringstream ss;
        ss << msg << ":"
           << std::scientific
           << std::showpoint
           << std::setprecision(16)
           << "expected " << expected
           << " actual " << actual
           << " with precision " << epsilon;
        throw failure(ss.str());
    }
}
/**
 * Tests two objects for being at most in given distance one from another.
 * Borders are excluded.
 * Throws if false.
 *
 * NB: T must have operator << defined somewhere, or
 * client code will not compile at all! Also, T shall have
 * operators + and -, and be comparable.
 *
 * TODO: domains are wrong, T - T might not yield T, but Q
 */
template <typename M, class T>
void ensure_distance(const M& msg, const T& actual, const T& expected, const T& distance)
{
    if (expected-distance >= actual || expected+distance <= actual)
    {
        std::stringstream ss;
        ss << (msg ? msg : "")
            << (msg? ":" : "")
            << " expected ("
            << expected-distance
            << " - "
            << expected+distance
            << ") actual '"
            << actual
            << '\'';
        throw failure(ss.str());
    }
}

template <class T>
void ensure_distance(const T& actual, const T& expected, const T& distance)
{
    ensure_distance<>("Distance is wrong", actual, expected, distance);
}

template<typename M>
void ensure_errno(const M& msg, bool cond)
{
    if(!cond)
    {
#if defined(TUT_USE_POSIX)
        char e[512];
        std::stringstream ss;
        ss << msg << ":"
           << strerror_r(errno, e, sizeof(e));
        throw failure(ss.str());
#else
        throw failure(msg);
#endif
    }
}

/**
 * Unconditionally fails with message.
 */
void fail(const char* msg = "")
{
    throw failure(msg);
}

template<typename M>
void fail(const M& msg)
{
    throw failure(msg);
}

} // end of namespace

}

#endif

<commit_msg>Fixed empty message check compilation bug<commit_after>#ifndef TUT_ASSERT_H_GUARD
#define TUT_ASSERT_H_GUARD

#include "tut_exception.hpp"
#include <limits>
#include <iomanip>

#if defined(TUT_USE_POSIX)
#include <errno.h>
#include <cstring>
#endif

namespace tut
{

    namespace detail
    {
        template<typename M>
        std::ostream &msg_prefix(std::ostream &str, const M &msg)
        {
            std::stringstream ss;
            ss << msg;

            if(!ss.str().empty())
            {
                str << ss.rdbuf() << ": ";
            }

            return str;
        }
    }

namespace
{

/**
 * Tests provided condition.
 * Throws if false.
 */
void ensure(bool cond)
{
    if (!cond)
    {
        // TODO: default ctor?
        throw failure("");
    }
}

/**
 * Tests provided condition.
 * Throws if true.
 */
void ensure_not(bool cond)
{
    ensure(!cond);
}

/**
 * Tests provided condition.
 * Throws if false.
 */
template <typename M>
void ensure(const M& msg, bool cond)
{
    if (!cond)
    {
        throw failure(msg);
    }
}

/**
 * Tests provided condition.
 * Throws if true.
 */
template <typename M>
void ensure_not(const M& msg, bool cond)
{
    ensure(msg, !cond);
}

/**
 * Tests two objects for being equal.
 * Throws if false.
 *
 * NB: both T and Q must have operator << defined somewhere, or
 * client code will not compile at all!
 */
template <typename M, typename LHS, typename RHS>
void ensure_equals(const M& msg, const LHS& actual, const RHS& expected)
{
    if (expected != actual)
    {
        std::stringstream ss;
        detail::msg_prefix(ss,msg)
           << "expected '"
           << expected
           << "' actual '"
           << actual
           << '\'';
        throw failure(ss.str());
    }
}

template <typename LHS, typename RHS>
void ensure_equals(const LHS& actual, const RHS& expected)
{
    ensure_equals("Values are not equal", actual, expected);
}

template<typename M>
void ensure_equals(const M& msg, const double& actual, const double& expected,
                   const double& epsilon = std::numeric_limits<double>::epsilon())
{
    const double diff = actual - expected;

    if ( !((diff <= epsilon) && (diff >= -epsilon )) )
    {
        std::stringstream ss;
        detail::msg_prefix(ss,msg)
           << std::scientific
           << std::showpoint
           << std::setprecision(16)
           << "expected " << expected
           << " actual " << actual
           << " with precision " << epsilon;
        throw failure(ss.str());
    }
}
/**
 * Tests two objects for being at most in given distance one from another.
 * Borders are excluded.
 * Throws if false.
 *
 * NB: T must have operator << defined somewhere, or
 * client code will not compile at all! Also, T shall have
 * operators + and -, and be comparable.
 *
 * TODO: domains are wrong, T - T might not yield T, but Q
 */
template <typename M, class T>
void ensure_distance(const M& msg, const T& actual, const T& expected, const T& distance)
{
    if (expected-distance >= actual || expected+distance <= actual)
    {
        std::stringstream ss;
        detail::msg_prefix(ss,msg)
            << " expected ("
            << expected-distance
            << " - "
            << expected+distance
            << ") actual '"
            << actual
            << '\'';
        throw failure(ss.str());
    }
}

template <class T>
void ensure_distance(const T& actual, const T& expected, const T& distance)
{
    ensure_distance<>("Distance is wrong", actual, expected, distance);
}

template<typename M>
void ensure_errno(const M& msg, bool cond)
{
    if(!cond)
    {
#if defined(TUT_USE_POSIX)
        char e[512];
        std::stringstream ss;
        detail::msg_prefix(ss,msg)
           << strerror_r(errno, e, sizeof(e));
        throw failure(ss.str());
#else
        throw failure(msg);
#endif
    }
}

/**
 * Unconditionally fails with message.
 */
void fail(const char* msg = "")
{
    throw failure(msg);
}

template<typename M>
void fail(const M& msg)
{
    throw failure(msg);
}

} // end of namespace

}

#endif

<|endoftext|>


<commit_before>
#include <map>
#include <algorithm>
#include <vector>
#include <random>
#include <ctime>

#include <glbinding/gl/functions.h>
#include <glbinding/gl/extension.h>
#include <glbinding/gl/enum.h>

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

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#include <globjects/globjects.h>
#include <globjects/logging.h>
#include <globjects/Texture.h>
#include <globjects/NamedString.h>

#include <globjects/base/File.h>

#include <common/Timer.h>
#include <common/Camera.h>
#include <common/AbstractCoordinateProvider.h>
#include <common/WorldInHandNavigation.h>
#include <common/Context.h>
#include <common/ContextFormat.h>
#include <common/Window.h>
#include <common/WindowEventHandler.h>
#include <common/events.h>

#include "AbstractParticleTechnique.h"

#include "ComputeShaderParticles.h"
#include "FragmentShaderParticles.h"
#include "TransformFeedbackParticles.h"

using namespace gl;
using namespace glm;
using namespace globjects;

class EventHandler : public WindowEventHandler, AbstractCoordinateProvider
{
public:
    EventHandler(int numParticles)
    : m_technique(ParticleTechnique::FragmentShaderTechnique)
    , m_numParticles(numParticles)
    , m_camera(nullptr)
    , m_steps(1)
    , m_nav(nullptr)
    {
        m_timer.setAutoUpdating(false);
        m_timer.start();
    }

    virtual ~EventHandler()
    {
        delete m_nav;
        delete m_camera;
    }

    virtual void initialize(Window & window) override
    {
        WindowEventHandler::initialize(window);

        // Initialize Particle Positions and Attributes

        m_positions.resize(m_numParticles);
        for (int i = 0; i < m_numParticles; ++i)
            m_positions[i] = vec4(sphericalRand<float>(1.f), 1.f);

        m_velocities.resize(m_numParticles);
        for (int i = 0; i < m_numParticles; ++i)
            m_velocities[i] = vec4(0.f);

        m_forces = Texture::createDefault(GL_TEXTURE_3D);

        NamedString::create("/particle-step.inc", new File("data/gpu-particles/particle-step.inc"));

        // initialize camera

        m_camera = new Camera(vec3( 0.f, 1.f,-3.f));
        m_camera->setZNear(1.f);
        m_camera->setZFar(16.f);

        m_nav = new  WorldInHandNavigation();
        m_nav->setCamera(m_camera);
        m_nav->setCoordinateProvider(this);

        // initialize techniques

        if (hasExtension(GLextension::GL_ARB_compute_shader))
            m_techniques[ParticleTechnique::ComputeShaderTechnique] = new ComputeShaderParticles(
                m_positions, m_velocities, *m_forces, *m_camera);
        else
            warning() << "Compute shader based implementation not supported.";

        if (hasExtension(GLextension::GL_ARB_transform_feedback3))
            m_techniques[ParticleTechnique::TransformFeedbackTechnique] = new TransformFeedbackParticles(
                m_positions, m_velocities, *m_forces, *m_camera);
        else
            warning() << "Transform feedback based implementation not supported.";

        m_techniques[ParticleTechnique::FragmentShaderTechnique] = new FragmentShaderParticles(
            m_positions, m_velocities, *m_forces, *m_camera);

        for (auto technique : m_techniques)
            if (technique.second)
                technique.second->initialize();

        reset();
    }

    virtual void framebufferResizeEvent(ResizeEvent & event) override
    {
        glViewport(0, 0, event.width(), event.height());

        m_camera->setViewport(event.size());

        for (auto technique : m_techniques)
            technique.second->resize();
    }

    virtual void paintEvent(PaintEvent & event) override
    {
        WindowEventHandler::paintEvent(event);

        draw();
    }

    void step(const float delta)
    {
        const float delta_stepped = delta / static_cast<float>(m_steps);

        for (int i = 0; i < m_steps; ++i)
            m_techniques[m_technique]->step(delta_stepped);
    }

    void draw()
    {
        const long double elapsed = static_cast<long double>(m_timer.elapsed().count());
        m_timer.update();

        const float delta = static_cast<float>((m_timer.elapsed().count() - elapsed) * 1.0e-9L);

        step(delta); // requires context to be current
        m_techniques[m_technique]->draw(delta);
    }

    void reset(const bool particles = true)
    {
        // initialize 3D Force Field (3D Texture)

        static const ivec3 fdim(8, 8, 8); // this has center axises and allows for random rings etc..

        std::vector<vec3> forces;
        forces.resize(fdim.x * fdim.y * fdim.z);

        srand(static_cast<unsigned int>(time(0)));

        for (int z = 0; z < fdim.z; ++z)
        for (int y = 0; y < fdim.y; ++y)
        for (int x = 0; x < fdim.x; ++x)
        {
            const int i = z *  fdim.x * fdim.y + y * fdim.x + x;
            const vec3 f(sphericalRand<float>(1.0));

            forces[i] = f * (1.f - length(vec3(x, y, z)) / std::sqrt(3.f)) * 10.f;
        }

        m_forces->image3D(0, GL_RGB32F, fdim.x, fdim.y, fdim.z, 0, GL_RGB, GL_FLOAT, forces.data());

        if (!particles)
            return;

        m_timer.reset();
        m_timer.update();

        for (auto technique : m_techniques)
            technique.second->reset();
    }

    // EVENT HANDLING

    virtual void keyPressEvent(KeyEvent & event) override
    {
        WindowEventHandler::keyPressEvent(event);

        switch (event.key())
        {
        case GLFW_KEY_C:
            if (m_techniques[ParticleTechnique::ComputeShaderTechnique])
            {
                debug() << "switch to compute shader technique";
                m_technique = ParticleTechnique::ComputeShaderTechnique;
            };
            break;

        case GLFW_KEY_T:
            if (m_techniques[ParticleTechnique::TransformFeedbackTechnique])
            {
                debug() << "switch to transform feedback technique";
                m_technique = ParticleTechnique::TransformFeedbackTechnique;
            }
            break;

        case GLFW_KEY_F:
            debug() << "switch to fragment shader technique";
            m_technique = ParticleTechnique::FragmentShaderTechnique;
            break;

        case GLFW_KEY_P:
            if (m_timer.paused())
            {
                debug() << "timer continue";
                m_timer.start();
            }
            else
            {
                debug() << "timer pause";
                m_timer.pause();
            }
            for (auto technique : m_techniques)
                if (technique.second)
                    technique.second->pause(m_timer.paused());

            break;

        case GLFW_KEY_R:
            reset(event.modifiers() & GLFW_MOD_SHIFT);
            break;

        case GLFW_KEY_MINUS:
            m_steps = max(1, m_steps - 1);
            debug() << "steps = " << m_steps;
            break;

        case GLFW_KEY_EQUAL: // bug? this is plus/add on my keyboard
            ++m_steps;
            debug() << "steps = " << m_steps;
            break;
        }
    }

    virtual void mousePressEvent(MouseEvent & event) override
    {
        switch (event.button())
        {
        case GLFW_MOUSE_BUTTON_LEFT:
            m_nav->rotateBegin(event.pos());
            event.accept();
            break;
        }
    }
    virtual void mouseMoveEvent(MouseEvent & event) override
    {
        switch (m_nav->mode())
        {
        case WorldInHandNavigation::RotateInteraction:
            m_nav->rotateProcess(event.pos());
            event.accept();
            break;

        case WorldInHandNavigation::PanInteraction:
        case WorldInHandNavigation::NoInteraction:
            break;
        }
    }
    virtual void mouseReleaseEvent(MouseEvent & event) override
    {
        switch (event.button())
        {
        case GLFW_MOUSE_BUTTON_LEFT:
            m_nav->rotateEnd();
            event.accept();
            break;
        }
    }

    virtual void scrollEvent(ScrollEvent & event) override
    {
        if (WorldInHandNavigation::NoInteraction != m_nav->mode())
            return;

        m_nav->scaleAtCenter(-event.offset().y * 0.1f);
        event.accept();
    }

    virtual float depthAt(const ivec2 & /*windowCoordinates*/) const override
    {
        return 2.f;
    }

    virtual vec3 objAt(const ivec2 & /*windowCoordinates*/) const override
    {
        return vec3(0.f);
    }
    virtual vec3 objAt(const ivec2 & /*windowCoordinates*/, const float /*depth*/) const override
    {
        return vec3(0.f);
    }
    virtual vec3 objAt(const ivec2 & /*windowCoordinates*/, const float /*depth*/, const mat4 & /*viewProjectionInverted*/) const override
    {
        return vec3(0.f);
    }

protected:

    enum class ParticleTechnique
    {
        ComputeShaderTechnique
    ,   FragmentShaderTechnique
    ,   TransformFeedbackTechnique
    };

    ParticleTechnique m_technique;
    std::map<ParticleTechnique, AbstractParticleTechnique *> m_techniques;

    Timer m_timer;

    int m_numParticles;
    Camera * m_camera;

    std::vector<vec4> m_positions;
    std::vector<vec4> m_velocities;

    int m_steps;

    WorldInHandNavigation * m_nav;

    struct Attribute
    {
        int moep;
    };
    std::vector<Attribute> m_attributes;

    ref_ptr<Texture> m_forces;
};

int main(int argc, char * argv[])
{
    info() << "Usage:";
    info() << "\t" << "ESC" << "\t\t"       << "Close example";
    info() << "\t" << "ALT + Enter" << "\t" << "Toggle fullscreen";
    info() << "\t" << "F11" << "\t\t"       << "Toggle fullscreen";
    info() << "\t" << "F10" << "\t\t"       << "Toggle vertical sync";
    info() << "\t" << "Left Mouse" << "\t"  << "Rotate scene";
    info() << "\t" << "Mouse Wheel" << "\t" << "Zoom scene";
    info() << "\t" << "-" << "\t\t"         << "Reduce steps per frame";
    info() << "\t" << "=" << "\t\t"         << "Increase steps per frame";
    info() << "\t" << "R" << "\t\t"         << "Compute new forces";
    info() << "\t" << "Shift + R" << "\t"   << "Compute new forces and reset particles";
    info() << "\t" << "P" << "\t\t"         << "Toggle pause";
    info() << "\t" << "F" << "\t\t"         << "Particle computation using fragment shader";
    info() << "\t" << "T" << "\t\t"         << "Particle computation using transform feedback";
    info() << "\t" << "C" << "\t\t"         << "Particle computation using compute shader";

    ContextFormat format;
    format.setVersion(3, 3); // minimum required version is 3.3 due to particle drawing using geometry shader.
    format.setProfile(ContextFormat::Profile::Core);

    Window::init();

    int numParticles = 262144;

    if (argc > 1) // assume that the argument is a number
        numParticles = atoi(argv[1]);

    Window window;
    window.setEventHandler(new EventHandler(numParticles));

    if (!window.create(format, "GPU - Particles Example"))
        return 1;

    window.show();

    return MainLoop::run();
}
<commit_msg>revert force field configuration of gpu-particles example<commit_after>
#include <map>
#include <algorithm>
#include <vector>
#include <random>
#include <ctime>

#include <glbinding/gl/functions.h>
#include <glbinding/gl/extension.h>
#include <glbinding/gl/enum.h>

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

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#include <globjects/globjects.h>
#include <globjects/logging.h>
#include <globjects/Texture.h>
#include <globjects/NamedString.h>

#include <globjects/base/File.h>

#include <common/Timer.h>
#include <common/Camera.h>
#include <common/AbstractCoordinateProvider.h>
#include <common/WorldInHandNavigation.h>
#include <common/Context.h>
#include <common/ContextFormat.h>
#include <common/Window.h>
#include <common/WindowEventHandler.h>
#include <common/events.h>

#include "AbstractParticleTechnique.h"

#include "ComputeShaderParticles.h"
#include "FragmentShaderParticles.h"
#include "TransformFeedbackParticles.h"

using namespace gl;
using namespace glm;
using namespace globjects;

class EventHandler : public WindowEventHandler, AbstractCoordinateProvider
{
public:
    EventHandler(int numParticles)
    : m_technique(ParticleTechnique::FragmentShaderTechnique)
    , m_numParticles(numParticles)
    , m_camera(nullptr)
    , m_steps(1)
    , m_nav(nullptr)
    {
        m_timer.setAutoUpdating(false);
        m_timer.start();
    }

    virtual ~EventHandler()
    {
        delete m_nav;
        delete m_camera;
    }

    virtual void initialize(Window & window) override
    {
        WindowEventHandler::initialize(window);

        // Initialize Particle Positions and Attributes

        m_positions.resize(m_numParticles);
        for (int i = 0; i < m_numParticles; ++i)
            m_positions[i] = vec4(sphericalRand<float>(1.f), 1.f);

        m_velocities.resize(m_numParticles);
        for (int i = 0; i < m_numParticles; ++i)
            m_velocities[i] = vec4(0.f);

        m_forces = Texture::createDefault(GL_TEXTURE_3D);

        NamedString::create("/particle-step.inc", new File("data/gpu-particles/particle-step.inc"));

        // initialize camera

        m_camera = new Camera(vec3( 0.f, 1.f,-3.f));
        m_camera->setZNear(1.f);
        m_camera->setZFar(16.f);

        m_nav = new  WorldInHandNavigation();
        m_nav->setCamera(m_camera);
        m_nav->setCoordinateProvider(this);

        // initialize techniques

        if (hasExtension(GLextension::GL_ARB_compute_shader))
            m_techniques[ParticleTechnique::ComputeShaderTechnique] = new ComputeShaderParticles(
                m_positions, m_velocities, *m_forces, *m_camera);
        else
            warning() << "Compute shader based implementation not supported.";

        if (hasExtension(GLextension::GL_ARB_transform_feedback3))
            m_techniques[ParticleTechnique::TransformFeedbackTechnique] = new TransformFeedbackParticles(
                m_positions, m_velocities, *m_forces, *m_camera);
        else
            warning() << "Transform feedback based implementation not supported.";

        m_techniques[ParticleTechnique::FragmentShaderTechnique] = new FragmentShaderParticles(
            m_positions, m_velocities, *m_forces, *m_camera);

        for (auto technique : m_techniques)
            if (technique.second)
                technique.second->initialize();

        reset();
    }

    virtual void framebufferResizeEvent(ResizeEvent & event) override
    {
        glViewport(0, 0, event.width(), event.height());

        m_camera->setViewport(event.size());

        for (auto technique : m_techniques)
            technique.second->resize();
    }

    virtual void paintEvent(PaintEvent & event) override
    {
        WindowEventHandler::paintEvent(event);

        draw();
    }

    void step(const float delta)
    {
        const float delta_stepped = delta / static_cast<float>(m_steps);

        for (int i = 0; i < m_steps; ++i)
            m_techniques[m_technique]->step(delta_stepped);
    }

    void draw()
    {
        const long double elapsed = static_cast<long double>(m_timer.elapsed().count());
        m_timer.update();

        const float delta = static_cast<float>((m_timer.elapsed().count() - elapsed) * 1.0e-9L);

        step(delta); // requires context to be current
        m_techniques[m_technique]->draw(delta);
    }

    void reset(const bool particles = true)
    {
        // initialize 3D Force Field (3D Texture)

        static const ivec3 fdim(5, 5, 5); // this has center axises and allows for random rings etc..

        std::vector<vec3> forces;
        forces.resize(fdim.x * fdim.y * fdim.z);

        srand(static_cast<unsigned int>(time(0)));

        for (int z = 0; z < fdim.z; ++z)
        for (int y = 0; y < fdim.y; ++y)
        for (int x = 0; x < fdim.x; ++x)
        {
            const int i = z *  fdim.x * fdim.y + y * fdim.x + x;
            const vec3 f(sphericalRand<float>(1.0));

            forces[i] = f * (1.f - length(vec3(x, y, z)) / std::sqrt(3.f));
        }

        m_forces->image3D(0, GL_RGB32F, fdim.x, fdim.y, fdim.z, 0, GL_RGB, GL_FLOAT, forces.data());

        if (!particles)
            return;

        m_timer.reset();
        m_timer.update();

        for (auto technique : m_techniques)
            technique.second->reset();
    }

    // EVENT HANDLING

    virtual void keyPressEvent(KeyEvent & event) override
    {
        WindowEventHandler::keyPressEvent(event);

        switch (event.key())
        {
        case GLFW_KEY_C:
            if (m_techniques[ParticleTechnique::ComputeShaderTechnique])
            {
                debug() << "switch to compute shader technique";
                m_technique = ParticleTechnique::ComputeShaderTechnique;
            };
            break;

        case GLFW_KEY_T:
            if (m_techniques[ParticleTechnique::TransformFeedbackTechnique])
            {
                debug() << "switch to transform feedback technique";
                m_technique = ParticleTechnique::TransformFeedbackTechnique;
            }
            break;

        case GLFW_KEY_F:
            debug() << "switch to fragment shader technique";
            m_technique = ParticleTechnique::FragmentShaderTechnique;
            break;

        case GLFW_KEY_P:
            if (m_timer.paused())
            {
                debug() << "timer continue";
                m_timer.start();
            }
            else
            {
                debug() << "timer pause";
                m_timer.pause();
            }
            for (auto technique : m_techniques)
                if (technique.second)
                    technique.second->pause(m_timer.paused());

            break;

        case GLFW_KEY_R:
            reset(event.modifiers() & GLFW_MOD_SHIFT);
            break;

        case GLFW_KEY_MINUS:
            m_steps = max(1, m_steps - 1);
            debug() << "steps = " << m_steps;
            break;

        case GLFW_KEY_EQUAL: // bug? this is plus/add on my keyboard
            ++m_steps;
            debug() << "steps = " << m_steps;
            break;
        }
    }

    virtual void mousePressEvent(MouseEvent & event) override
    {
        switch (event.button())
        {
        case GLFW_MOUSE_BUTTON_LEFT:
            m_nav->rotateBegin(event.pos());
            event.accept();
            break;
        }
    }
    virtual void mouseMoveEvent(MouseEvent & event) override
    {
        switch (m_nav->mode())
        {
        case WorldInHandNavigation::RotateInteraction:
            m_nav->rotateProcess(event.pos());
            event.accept();
            break;

        case WorldInHandNavigation::PanInteraction:
        case WorldInHandNavigation::NoInteraction:
            break;
        }
    }
    virtual void mouseReleaseEvent(MouseEvent & event) override
    {
        switch (event.button())
        {
        case GLFW_MOUSE_BUTTON_LEFT:
            m_nav->rotateEnd();
            event.accept();
            break;
        }
    }

    virtual void scrollEvent(ScrollEvent & event) override
    {
        if (WorldInHandNavigation::NoInteraction != m_nav->mode())
            return;

        m_nav->scaleAtCenter(-event.offset().y * 0.1f);
        event.accept();
    }

    virtual float depthAt(const ivec2 & /*windowCoordinates*/) const override
    {
        return 2.f;
    }

    virtual vec3 objAt(const ivec2 & /*windowCoordinates*/) const override
    {
        return vec3(0.f);
    }
    virtual vec3 objAt(const ivec2 & /*windowCoordinates*/, const float /*depth*/) const override
    {
        return vec3(0.f);
    }
    virtual vec3 objAt(const ivec2 & /*windowCoordinates*/, const float /*depth*/, const mat4 & /*viewProjectionInverted*/) const override
    {
        return vec3(0.f);
    }

protected:

    enum class ParticleTechnique
    {
        ComputeShaderTechnique
    ,   FragmentShaderTechnique
    ,   TransformFeedbackTechnique
    };

    ParticleTechnique m_technique;
    std::map<ParticleTechnique, AbstractParticleTechnique *> m_techniques;

    Timer m_timer;

    int m_numParticles;
    Camera * m_camera;

    std::vector<vec4> m_positions;
    std::vector<vec4> m_velocities;

    int m_steps;

    WorldInHandNavigation * m_nav;

    struct Attribute
    {
        int moep;
    };
    std::vector<Attribute> m_attributes;

    ref_ptr<Texture> m_forces;
};

int main(int argc, char * argv[])
{
    info() << "Usage:";
    info() << "\t" << "ESC" << "\t\t"       << "Close example";
    info() << "\t" << "ALT + Enter" << "\t" << "Toggle fullscreen";
    info() << "\t" << "F11" << "\t\t"       << "Toggle fullscreen";
    info() << "\t" << "F10" << "\t\t"       << "Toggle vertical sync";
    info() << "\t" << "Left Mouse" << "\t"  << "Rotate scene";
    info() << "\t" << "Mouse Wheel" << "\t" << "Zoom scene";
    info() << "\t" << "-" << "\t\t"         << "Reduce steps per frame";
    info() << "\t" << "=" << "\t\t"         << "Increase steps per frame";
    info() << "\t" << "R" << "\t\t"         << "Compute new forces";
    info() << "\t" << "Shift + R" << "\t"   << "Compute new forces and reset particles";
    info() << "\t" << "P" << "\t\t"         << "Toggle pause";
    info() << "\t" << "F" << "\t\t"         << "Particle computation using fragment shader";
    info() << "\t" << "T" << "\t\t"         << "Particle computation using transform feedback";
    info() << "\t" << "C" << "\t\t"         << "Particle computation using compute shader";

    ContextFormat format;
    format.setVersion(3, 3); // minimum required version is 3.3 due to particle drawing using geometry shader.
    format.setProfile(ContextFormat::Profile::Core);

    Window::init();

    int numParticles = 262144;

    if (argc > 1) // assume that the argument is a number
        numParticles = atoi(argv[1]);

    Window window;
    window.setEventHandler(new EventHandler(numParticles));

    if (!window.create(format, "GPU - Particles Example"))
        return 1;

    window.show();

    return MainLoop::run();
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "load/param.hh"

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>

#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/uuid/uuid.hpp>

#include "lo.pb.h"

#include "bc/index.h"
#include "bc/pack.h"

using std::cerr;
using std::endl;

namespace flint {
namespace load {

namespace {

struct State {
	State()
		: pos(kOffsetBase)
	{}

	int pos;
	boost::ptr_vector<lo::Column> columns;
};

int AddColumn(void *data, int argc, char **argv, char **names)
{
	State *state = static_cast<State *>(data);
	(void)names;
	assert(argc == 9);
	std::unique_ptr<lo::Column> c(new lo::Column);
	c->set_position(state->pos);
	c->set_size(1); // TODO: variable size
	assert(argv[2]);
	c->set_uuid(argv[2], boost::uuids::uuid::static_size()); // sector_id
	c->set_name(argv[4]); // name
	assert(argv[5]);
	assert(std::strlen(argv[5]) == 1);
	switch (argv[5][0]) { // type
	case 's':
		c->set_type(lo::S);
		break;
	case 'x':
		c->set_type(lo::X);
		break;
	default:
		state->pos += 1; // TODO: variable size
		return 0;
	}
	c->set_id(std::atoi(argv[6])); // id
	c->set_unit(argv[7]); // unit
	if (argv[1]) {
		c->set_track_name(argv[1]);
	}
	if (argv[3]) { // label
		c->set_label(argv[3]);
	}
	state->columns.push_back(c.release());
	state->pos += 1; // TODO: variable size
	return 0;
}

bool Process(sqlite3 *db, State *state)
{
	char *em;
	int e;
	e = sqlite3_exec(db, "SELECT * FROM layout",
					 &AddColumn, state, &em);
	if (e != SQLITE_OK) {
		cerr << "failed to select layout: " << e
			 << ": " << em << endl;
		sqlite3_free(em);
		return false;
	}
	return true;
}

}

bool Param(sqlite3 *db, const char *output)
{
	State state;
	if (!Process(db, &state))
		return false;
	lo::Header header;
	header.set_size(state.pos);

	std::ofstream ofs(output, std::ios::out|std::ios::binary);
	if (!ofs) {
		cerr << "failed to open " << output << endl;
		return false;
	}
	if (!PackToOstream(header, &ofs)) {
		cerr << "failed to pack Header" << endl;
		return false;
	}
	for (boost::ptr_vector<lo::Column>::const_iterator it=state.columns.begin();it!=state.columns.end();++it) {
		if (!PackToOstream(*it, &ofs)) {
			cerr << "failed to pack Column" << endl;
			return false;
		}
	}
	ofs.close();

	return true;
}

}
}
<commit_msg>Replace std::ptr_vector with std::vector<std::unique_ptr><commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "load/param.hh"

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <vector>

#include <boost/uuid/uuid.hpp>

#include "lo.pb.h"

#include "bc/index.h"
#include "bc/pack.h"

using std::cerr;
using std::endl;

namespace flint {
namespace load {

namespace {

struct State {
	State()
		: pos(kOffsetBase)
	{}

	int pos;
	std::vector<std::unique_ptr<lo::Column> > columns;
};

int AddColumn(void *data, int argc, char **argv, char **names)
{
	State *state = static_cast<State *>(data);
	(void)names;
	assert(argc == 9);
	std::unique_ptr<lo::Column> c(new lo::Column);
	c->set_position(state->pos);
	c->set_size(1); // TODO: variable size
	assert(argv[2]);
	c->set_uuid(argv[2], boost::uuids::uuid::static_size()); // sector_id
	c->set_name(argv[4]); // name
	assert(argv[5]);
	assert(std::strlen(argv[5]) == 1);
	switch (argv[5][0]) { // type
	case 's':
		c->set_type(lo::S);
		break;
	case 'x':
		c->set_type(lo::X);
		break;
	default:
		state->pos += 1; // TODO: variable size
		return 0;
	}
	c->set_id(std::atoi(argv[6])); // id
	c->set_unit(argv[7]); // unit
	if (argv[1]) {
		c->set_track_name(argv[1]);
	}
	if (argv[3]) { // label
		c->set_label(argv[3]);
	}
	state->columns.push_back(std::move(c));
	state->pos += 1; // TODO: variable size
	return 0;
}

bool Process(sqlite3 *db, State *state)
{
	char *em;
	int e;
	e = sqlite3_exec(db, "SELECT * FROM layout",
					 &AddColumn, state, &em);
	if (e != SQLITE_OK) {
		cerr << "failed to select layout: " << e
			 << ": " << em << endl;
		sqlite3_free(em);
		return false;
	}
	return true;
}

}

bool Param(sqlite3 *db, const char *output)
{
	State state;
	if (!Process(db, &state))
		return false;
	lo::Header header;
	header.set_size(state.pos);

	std::ofstream ofs(output, std::ios::out|std::ios::binary);
	if (!ofs) {
		cerr << "failed to open " << output << endl;
		return false;
	}
	if (!PackToOstream(header, &ofs)) {
		cerr << "failed to pack Header" << endl;
		return false;
	}
	for (auto it=state.columns.cbegin();it!=state.columns.cend();++it) {
		if (!PackToOstream(**it, &ofs)) {
			cerr << "failed to pack Column" << endl;
			return false;
		}
	}
	ofs.close();

	return true;
}

}
}
<|endoftext|>