<commit_before>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include "Sdl2Application.h"

#include <Utility/utilities.h>

#include "Context.h"
#include "ExtensionWrangler.h"

namespace Magnum { namespace Platform {

namespace {

/*
 * Fix up the modifiers -- we want >= operator to work properly on Shift,
 * Ctrl, Alt, but SDL generates different event for left / right keys, thus
 * (modifiers >= Shift) would pass only if both left and right were pressed,
 * which is usually not what the developers wants.
 */
Sdl2Application::InputEvent::Modifiers fixedModifiers(Uint16 mod) {
    Sdl2Application::InputEvent::Modifiers modifiers(static_cast<Sdl2Application::InputEvent::Modifier>(mod));
    if(modifiers & Sdl2Application::InputEvent::Modifier::Shift) modifiers |= Sdl2Application::InputEvent::Modifier::Shift;
    if(modifiers & Sdl2Application::InputEvent::Modifier::Ctrl) modifiers |= Sdl2Application::InputEvent::Modifier::Ctrl;
    if(modifiers & Sdl2Application::InputEvent::Modifier::Alt) modifiers |= Sdl2Application::InputEvent::Modifier::Alt;
    return modifiers;
}

}

Sdl2Application::Sdl2Application(const Arguments&): context(nullptr), flags(Flag::Redraw) {
    initialize();
    createContext(new Configuration);
}

Sdl2Application::Sdl2Application(const Arguments&, Configuration* configuration): context(nullptr), flags(Flag::Redraw) {
    initialize();
    if(configuration) createContext(configuration);
}

void Sdl2Application::initialize() {
    if(SDL_Init(SDL_INIT_VIDEO) < 0) {
        Error() << "Cannot initialize SDL.";
        std::exit(1);
    }
}

void Sdl2Application::createContext(Configuration* configuration) {
    if(!tryCreateContext(configuration)) {
        Error() << "Platform::Sdl2Application::createContext(): cannot create context:" << SDL_GetError();
        delete configuration;
        std::exit(1);

    } else delete configuration;
}

bool Sdl2Application::tryCreateContext(Configuration* configuration) {
    CORRADE_ASSERT(!context, "Platform::Sdl2Application::tryCreateContext(): context already created", false);

    /* Enable double buffering and 24bt depth buffer */
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    /* Multisampling */
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, configuration->sampleCount() > 1 ? 1 : 0);
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, configuration->sampleCount());

    /* Flags: if not hidden, set as shown */
    Uint32 flags(configuration->flags());
    if(!(configuration->flags() & Configuration::Flag::Hidden)) flags |= SDL_WINDOW_SHOWN;

    if(!(window = SDL_CreateWindow(configuration->title().c_str(),
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            configuration->size().x(), configuration->size().y(),
            SDL_WINDOW_OPENGL|flags))) {
        Error() << "Platform::Sdl2Application::tryCreateContext(): cannot create window:" << SDL_GetError();
        std::exit(2);
    }

    if(!(context = SDL_GL_CreateContext(window))) {
        SDL_DestroyWindow(window);
        window = nullptr;
        return false;
    }

    /* This must be enabled, otherwise (on my NVidia) it crashes when creating
       VAO. WTF. */
    ExtensionWrangler::initialize(ExtensionWrangler::ExperimentalFeatures::Enable);

    /* Push resize event, so viewportEvent() is called at startup */
    SDL_Event* sizeEvent = new SDL_Event;
    sizeEvent->type = SDL_WINDOWEVENT;
    sizeEvent->window.event = SDL_WINDOWEVENT_RESIZED;
    sizeEvent->window.data1 = configuration->size().x();
    sizeEvent->window.data2 = configuration->size().y();
    SDL_PushEvent(sizeEvent);

    c = new Context;
    return true;
}

Sdl2Application::~Sdl2Application() {
    delete c;

    SDL_GL_DeleteContext(context);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

int Sdl2Application::exec() {
    while(!(flags & Flag::Exit)) {
        SDL_Event event;

        while(SDL_PollEvent(&event)) {
            switch(event.type) {
                case SDL_WINDOWEVENT:
                    switch(event.window.event) {
                        case SDL_WINDOWEVENT_RESIZED:
                            viewportEvent({event.window.data1, event.window.data2});
                            flags |= Flag::Redraw;
                            break;
                        case SDL_WINDOWEVENT_EXPOSED:
                            flags |= Flag::Redraw;
                            break;
                    } break;

                case SDL_KEYDOWN:
                case SDL_KEYUP: {
                    KeyEvent e(static_cast<KeyEvent::Key>(event.key.keysym.sym), fixedModifiers(event.key.keysym.mod));
                    event.type == SDL_KEYDOWN ? keyPressEvent(e) : keyReleaseEvent(e);
                } break;

                case SDL_MOUSEBUTTONDOWN:
                case SDL_MOUSEBUTTONUP: {
                    MouseEvent e(static_cast<MouseEvent::Button>(event.button.button), {event.button.x, event.button.y});
                    event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e);
                } break;

                case SDL_MOUSEWHEEL:
                    if(event.wheel.y != 0) {
                        MouseEvent e(event.wheel.y < 0 ? MouseEvent::Button::WheelUp : MouseEvent::Button::WheelDown, {event.wheel.x, event.wheel.y});
                        mousePressEvent(e);
                    } break;

                case SDL_MOUSEMOTION: {
                    MouseMoveEvent e({event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel});
                    mouseMoveEvent(e);
                    break;
                }

                case SDL_QUIT: return 0;
            }
        }

        if(flags & Flag::Redraw) {
            flags &= ~Flag::Redraw;
            drawEvent();
        } else Corrade::Utility::sleep(5);
    }

    return 0;
}

void Sdl2Application::setMouseLocked(bool enabled) {
    SDL_SetWindowGrab(window, enabled ? SDL_TRUE : SDL_FALSE);
    SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE);
}

Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600), _flags(Flag::Resizable), _sampleCount(0) {}
Sdl2Application::Configuration::~Configuration() = default;

Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() {
    if(modifiersLoaded) return _modifiers;
    modifiersLoaded = true;
    return _modifiers = fixedModifiers(SDL_GetModState());
}

Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseMoveEvent::modifiers() {
    if(modifiersLoaded) return _modifiers;
    modifiersLoaded = true;
    return _modifiers = fixedModifiers(SDL_GetModState());
}

}}
<commit_msg>Platform: fixed Sdl2Application::tryCreateContext().<commit_after>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include "Sdl2Application.h"

#include <Utility/utilities.h>

#include "Context.h"
#include "ExtensionWrangler.h"

namespace Magnum { namespace Platform {

namespace {

/*
 * Fix up the modifiers -- we want >= operator to work properly on Shift,
 * Ctrl, Alt, but SDL generates different event for left / right keys, thus
 * (modifiers >= Shift) would pass only if both left and right were pressed,
 * which is usually not what the developers wants.
 */
Sdl2Application::InputEvent::Modifiers fixedModifiers(Uint16 mod) {
    Sdl2Application::InputEvent::Modifiers modifiers(static_cast<Sdl2Application::InputEvent::Modifier>(mod));
    if(modifiers & Sdl2Application::InputEvent::Modifier::Shift) modifiers |= Sdl2Application::InputEvent::Modifier::Shift;
    if(modifiers & Sdl2Application::InputEvent::Modifier::Ctrl) modifiers |= Sdl2Application::InputEvent::Modifier::Ctrl;
    if(modifiers & Sdl2Application::InputEvent::Modifier::Alt) modifiers |= Sdl2Application::InputEvent::Modifier::Alt;
    return modifiers;
}

}

Sdl2Application::Sdl2Application(const Arguments&): context(nullptr), flags(Flag::Redraw) {
    initialize();
    createContext(new Configuration);
}

Sdl2Application::Sdl2Application(const Arguments&, Configuration* configuration): context(nullptr), flags(Flag::Redraw) {
    initialize();
    if(configuration) createContext(configuration);
}

void Sdl2Application::initialize() {
    if(SDL_Init(SDL_INIT_VIDEO) < 0) {
        Error() << "Cannot initialize SDL.";
        std::exit(1);
    }
}

void Sdl2Application::createContext(Configuration* configuration) {
    if(!tryCreateContext(configuration)) {
        Error() << "Platform::Sdl2Application::createContext(): cannot create context:" << SDL_GetError();
        delete configuration;
        std::exit(1);

    } else delete configuration;
}

bool Sdl2Application::tryCreateContext(Configuration* configuration) {
    CORRADE_ASSERT(!context, "Platform::Sdl2Application::tryCreateContext(): context already created", false);

    /* Enable double buffering and 24bt depth buffer */
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

    /* Multisampling */
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, configuration->sampleCount() > 1 ? 1 : 0);
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, configuration->sampleCount());

    /* Flags: if not hidden, set as shown */
    Uint32 flags(configuration->flags());
    if(!(configuration->flags() & Configuration::Flag::Hidden)) flags |= SDL_WINDOW_SHOWN;

    if(!(window = SDL_CreateWindow(configuration->title().c_str(),
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            configuration->size().x(), configuration->size().y(),
            SDL_WINDOW_OPENGL|flags)))
        return false;

    if(!(context = SDL_GL_CreateContext(window))) {
        SDL_DestroyWindow(window);
        window = nullptr;
        return false;
    }

    /* This must be enabled, otherwise (on my NVidia) it crashes when creating
       VAO. WTF. */
    ExtensionWrangler::initialize(ExtensionWrangler::ExperimentalFeatures::Enable);

    /* Push resize event, so viewportEvent() is called at startup */
    SDL_Event* sizeEvent = new SDL_Event;
    sizeEvent->type = SDL_WINDOWEVENT;
    sizeEvent->window.event = SDL_WINDOWEVENT_RESIZED;
    sizeEvent->window.data1 = configuration->size().x();
    sizeEvent->window.data2 = configuration->size().y();
    SDL_PushEvent(sizeEvent);

    c = new Context;
    return true;
}

Sdl2Application::~Sdl2Application() {
    delete c;

    SDL_GL_DeleteContext(context);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

int Sdl2Application::exec() {
    while(!(flags & Flag::Exit)) {
        SDL_Event event;

        while(SDL_PollEvent(&event)) {
            switch(event.type) {
                case SDL_WINDOWEVENT:
                    switch(event.window.event) {
                        case SDL_WINDOWEVENT_RESIZED:
                            viewportEvent({event.window.data1, event.window.data2});
                            flags |= Flag::Redraw;
                            break;
                        case SDL_WINDOWEVENT_EXPOSED:
                            flags |= Flag::Redraw;
                            break;
                    } break;

                case SDL_KEYDOWN:
                case SDL_KEYUP: {
                    KeyEvent e(static_cast<KeyEvent::Key>(event.key.keysym.sym), fixedModifiers(event.key.keysym.mod));
                    event.type == SDL_KEYDOWN ? keyPressEvent(e) : keyReleaseEvent(e);
                } break;

                case SDL_MOUSEBUTTONDOWN:
                case SDL_MOUSEBUTTONUP: {
                    MouseEvent e(static_cast<MouseEvent::Button>(event.button.button), {event.button.x, event.button.y});
                    event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e);
                } break;

                case SDL_MOUSEWHEEL:
                    if(event.wheel.y != 0) {
                        MouseEvent e(event.wheel.y < 0 ? MouseEvent::Button::WheelUp : MouseEvent::Button::WheelDown, {event.wheel.x, event.wheel.y});
                        mousePressEvent(e);
                    } break;

                case SDL_MOUSEMOTION: {
                    MouseMoveEvent e({event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel});
                    mouseMoveEvent(e);
                    break;
                }

                case SDL_QUIT: return 0;
            }
        }

        if(flags & Flag::Redraw) {
            flags &= ~Flag::Redraw;
            drawEvent();
        } else Corrade::Utility::sleep(5);
    }

    return 0;
}

void Sdl2Application::setMouseLocked(bool enabled) {
    SDL_SetWindowGrab(window, enabled ? SDL_TRUE : SDL_FALSE);
    SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE);
}

Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600), _flags(Flag::Resizable), _sampleCount(0) {}
Sdl2Application::Configuration::~Configuration() = default;

Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() {
    if(modifiersLoaded) return _modifiers;
    modifiersLoaded = true;
    return _modifiers = fixedModifiers(SDL_GetModState());
}

Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseMoveEvent::modifiers() {
    if(modifiersLoaded) return _modifiers;
    modifiersLoaded = true;
    return _modifiers = fixedModifiers(SDL_GetModState());
}

}}
<|endoftext|>


<commit_before>/*
   Copyright 2012-2015 Alex Zhondin <qtinuum.team@gmail.com>
   Copyright 2015-2016 Alexandra Cherdantseva <neluhus.vagus@gmail.com>

   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.
*/

#include "QObjectPropertySet.h"
#include "PropertyCore.h"
#include "PropertyGUI.h"
#include "PropertyConnector.h"
#include "MultiProperty.h"
#include "IQtnPropertyStateProvider.h"
#include "Config.h"

#include <QCoreApplication>
#include <QMetaObject>
#include <QMetaProperty>
#include <QMap>
#include <QLocale>

static QtnProperty *createRealNumberProperty(
	QObject *object, const
	QMetaProperty &metaProperty)
{
	auto property = new QtnPropertyDoubleCallback(nullptr);
	switch (metaProperty.revision())
	{
		case PERCENT_SUFFIX:
		{
			QtnPropertyDelegateInfo delegate;
			qtnInitPercentSpinBoxDelegate(delegate);
			property->setDelegate(delegate);

			property->setCallbackValueGet(
				[object, metaProperty]() -> qreal
			{
				return metaProperty.read(object).toDouble() * 100.0;
			});

			property->setCallbackValueSet(
				[object, metaProperty](qreal value)
			{
				metaProperty.write(object, value / 100.0);
			});

			property->setCallbackValueAccepted(
				[property](qreal) -> bool
			{
				return property->isEditableByUser();
			});

			return property;
		}

		case DEGREE_SUFFIX:
		{
			QtnPropertyDelegateInfo delegate;
			qtnInitDegreeSpinBoxDelegate(delegate);
			property->setDelegate(delegate);
			break;
		}
	}

	property->setCallbackValueGet(
		[object, metaProperty]() -> qreal
	{
		return metaProperty.read(object).toDouble();
	});

	property->setCallbackValueSet(
		[object, metaProperty](qreal value)
	{
		metaProperty.write(object, value);
	});

	property->setCallbackValueAccepted(
		[property](qreal) -> bool
	{
		return property->isEditableByUser();
	});

	return property;
}

bool qtnRegisterDefaultMetaPropertyFactory()
{
	qtnRegisterMetaPropertyFactory(
		QVariant::Bool,
		qtnCreateFactory<QtnPropertyBoolCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::String,
		qtnCreateFactory<QtnPropertyQStringCallback>());
	qtnRegisterMetaPropertyFactory(QVariant::Double, createRealNumberProperty);
	qtnRegisterMetaPropertyFactory(
		QVariant::Int,
		qtnCreateFactory<QtnPropertyIntCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::UInt,
		qtnCreateFactory<QtnPropertyUIntCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Point,
		qtnCreateFactory<QtnPropertyQPointCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Rect,
		qtnCreateFactory<QtnPropertyQRectCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Size,
		qtnCreateFactory<QtnPropertyQSizeCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Color,
		qtnCreateFactory<QtnPropertyQColorCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Font,
		qtnCreateFactory<QtnPropertyQFontCallback>());
	return true;
}

static QMap<int, QtnMetaPropertyFactory_t> qtnFactoryMap;
static bool success = qtnRegisterDefaultMetaPropertyFactory();

bool qtnRegisterMetaPropertyFactory(
	int metaPropertyType, const
	QtnMetaPropertyFactory_t &factory, bool force)
{
	Q_ASSERT(factory);

	if (!force && qtnFactoryMap.contains(metaPropertyType))
		return false;

	qtnFactoryMap.insert(metaPropertyType, factory);
	return true;
}

QtnPropertyState qtnPropertyStateToAdd(const QMetaProperty &metaProperty)
{
	QtnPropertyState toAdd;

	if (!metaProperty.isDesignable())
		toAdd |= QtnPropertyStateInvisible;

	if (metaProperty.isConstant()
		|| (!metaProperty.isWritable() && !metaProperty.isResettable()))
	{
		toAdd |= QtnPropertyStateImmutable;
	}

	return toAdd;
}

void qtnUpdatePropertyState(
	QtnPropertyBase *property, const QMetaProperty &metaProperty)
{
	property->addState(qtnPropertyStateToAdd(metaProperty));
}

QtnProperty *qtnCreateQObjectProperty(
	QObject *object, const QMetaProperty &metaProperty,
	bool connect, const char *className)
{
	if (!object)
		return nullptr;

	auto it = qtnFactoryMap.find(metaProperty.type());
	if (it == qtnFactoryMap.end())
		it = qtnFactoryMap.find(metaProperty.userType());
	if (it == qtnFactoryMap.end())
		return nullptr;

	if (!metaProperty.isDesignable(object) || !metaProperty.isReadable())
		return nullptr;

	QtnProperty *property = it.value() (object, metaProperty);
	if (!property)
		return property;

	property->setName(
		(nullptr != className)
		? QCoreApplication::translate(className, metaProperty.name())
		: metaProperty.name());

	auto stateProvider = dynamic_cast<IQtnPropertyStateProvider *>(object);
	if (nullptr != stateProvider)
	{
		auto state = stateProvider->getPropertyState(metaProperty);
		property->setState(state);
	}

	qtnUpdatePropertyState(property, metaProperty);

	if (connect && metaProperty.hasNotifySignal())
	{
		auto connector = new QtnPropertyConnector(property);
		connector->connectProperty(object, metaProperty);
	}

	return property;
}

QtnProperty *qtnCreateQObjectProperty(
	QObject *object, const char *propertyName, bool connect)
{
	if (!object)
		return nullptr;

	const QMetaObject *metaObject = object->metaObject();
	int propertyIndex = -1;

	while (metaObject)
	{
		propertyIndex = object->metaObject()->indexOfProperty(propertyName);
		if (propertyIndex != -1)
			break;

		metaObject = metaObject->superClass();
	}

	if (!metaObject)
		return nullptr;

	if (propertyIndex == -1)
		return nullptr;

	Q_ASSERT(propertyIndex >= 0 && propertyIndex < metaObject->propertyCount());

	return qtnCreateQObjectProperty(
		object, metaObject->property(propertyIndex), connect);
}

QtnPropertySet *qtnCreateQObjectPropertySet(QObject *object)
{
	if (!object)
		return nullptr;

	// collect property sets by object's classes
	QStringList classNames;
	std::map<QString, QtnPropertySet *> propertySetsByClass;

	auto metaObject = object->metaObject();
	while (nullptr != metaObject)
	{
		if (metaObject->propertyCount() > 0)
		{
			QList<QtnProperty *> properties;
			for (int propertyIndex = metaObject->propertyOffset(),
				 n = metaObject->propertyCount();
				 propertyIndex < n; ++propertyIndex)
			{
				auto metaProperty = metaObject->property(propertyIndex);
				auto property = qtnCreateQObjectProperty(
						object, metaProperty, true, metaObject->className());
				if (nullptr != property)
					properties.append(property);
			}

			if (!properties.isEmpty())
			{
				auto className = QCoreApplication::translate(
						"ClassName",
						metaObject->className());
				auto it = propertySetsByClass.find(className);

				QtnPropertySet *propertySetByClass;
				if (it != propertySetsByClass.end())
					propertySetByClass = it->second;
				else
				{
					propertySetByClass = new QtnPropertySet(nullptr);
					propertySetByClass->setName(className);
					propertySetsByClass[className] = propertySetByClass;

					classNames.push_back(className);
				}

				for (auto property : properties)
				{
					propertySetByClass->addChildProperty(property);
				}
			}
		}

		// move up in class hierarchy
		metaObject = metaObject->superClass();
	}

	if (propertySetsByClass.empty())
		return nullptr;

	// move collected property sets to object's property set
	auto propertySet = new QtnPropertySet(nullptr);
	propertySet->setName(object->objectName());

	for (auto &class_name : classNames)
	{
		propertySet->addChildProperty(propertySetsByClass[class_name]);
	}

	return propertySet;
}

QtnPropertySet *qtnCreateQObjectMultiPropertySet(
	const
	std::set<QObject *> &objects)
{
	if (objects.empty())
		return nullptr;

	auto result = new QtnPropertySet(nullptr);
	auto &subSets = result->childProperties();

	for (auto object : objects)
	{
		auto propertySet = qtnCreateQObjectPropertySet(object);
		if (nullptr == propertySet)
			continue;

		for (int i = propertySet->childProperties().count() - 1;
			 i >= 0; i--)
		{
			auto property = propertySet->childProperties().at(i);

			auto subSet = dynamic_cast<QtnPropertySet *>(property);
			Q_ASSERT(nullptr != subSet);

			auto it = std::find_if(
					subSets.begin(), subSets.end(),
					[subSet](const QtnPropertyBase *set) -> bool
			{
				return (subSet->name() == set->name());
			});

			QtnPropertySet *multiSet;
			if (it == subSets.end())
			{
				multiSet = new QtnPropertySet(nullptr);
				multiSet->setName(subSet->name());
				multiSet->setDescription(subSet->description());
				multiSet->setId(subSet->id());
				multiSet->setState(subSet->stateLocal());

				result->addChildProperty(multiSet, true, 0);
			} else
			{
				multiSet = dynamic_cast<QtnPropertySet *>(*it);
			}

			auto &ncp = multiSet->childProperties();
			for (auto subProp : subSet->childProperties())
			{
				auto propIt = std::find_if(
						ncp.begin(), ncp.end(),
						[subProp](QtnPropertyBase *p) -> bool
				{
					return p->name() == subProp->name();
				});

				QtnMultiProperty *multiProperty;
				if (propIt == ncp.end())
				{
					multiProperty = new QtnMultiProperty(subProp->metaObject());
					multiProperty->setName(subProp->name());
					multiProperty->setDescription(subProp->description());
					multiProperty->setId(subProp->id());

					multiSet->addChildProperty(multiProperty);
				} else
				{
					multiProperty = dynamic_cast<QtnMultiProperty *>(*propIt);
				}

				multiProperty->addProperty(
					dynamic_cast<QtnProperty *>(subProp));
			}
		}

		delete propertySet;
	}

	return result;
}

void qtnInitPercentSpinBoxDelegate(QtnPropertyDelegateInfo &delegate)
{
	delegate.name = "SpinBox";
	delegate.attributes["suffix"] = QLocale().percent();
}

void qtnInitDegreeSpinBoxDelegate(QtnPropertyDelegateInfo &delegate)
{
	delegate.name = "SpinBox";
	delegate.attributes["suffix"] = QString::fromUtf8("º");
}
<commit_msg>QObject property create with class name<commit_after>/*
   Copyright 2012-2015 Alex Zhondin <qtinuum.team@gmail.com>
   Copyright 2015-2016 Alexandra Cherdantseva <neluhus.vagus@gmail.com>

   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.
*/

#include "QObjectPropertySet.h"
#include "PropertyCore.h"
#include "PropertyGUI.h"
#include "PropertyConnector.h"
#include "MultiProperty.h"
#include "IQtnPropertyStateProvider.h"
#include "Config.h"

#include <QCoreApplication>
#include <QMetaObject>
#include <QMetaProperty>
#include <QMap>
#include <QLocale>

static QtnProperty *createRealNumberProperty(
	QObject *object, const
	QMetaProperty &metaProperty)
{
	auto property = new QtnPropertyDoubleCallback(nullptr);
	switch (metaProperty.revision())
	{
		case PERCENT_SUFFIX:
		{
			QtnPropertyDelegateInfo delegate;
			qtnInitPercentSpinBoxDelegate(delegate);
			property->setDelegate(delegate);

			property->setCallbackValueGet(
				[object, metaProperty]() -> qreal
			{
				return metaProperty.read(object).toDouble() * 100.0;
			});

			property->setCallbackValueSet(
				[object, metaProperty](qreal value)
			{
				metaProperty.write(object, value / 100.0);
			});

			property->setCallbackValueAccepted(
				[property](qreal) -> bool
			{
				return property->isEditableByUser();
			});

			return property;
		}

		case DEGREE_SUFFIX:
		{
			QtnPropertyDelegateInfo delegate;
			qtnInitDegreeSpinBoxDelegate(delegate);
			property->setDelegate(delegate);
			break;
		}
	}

	property->setCallbackValueGet(
		[object, metaProperty]() -> qreal
	{
		return metaProperty.read(object).toDouble();
	});

	property->setCallbackValueSet(
		[object, metaProperty](qreal value)
	{
		metaProperty.write(object, value);
	});

	property->setCallbackValueAccepted(
		[property](qreal) -> bool
	{
		return property->isEditableByUser();
	});

	return property;
}

bool qtnRegisterDefaultMetaPropertyFactory()
{
	qtnRegisterMetaPropertyFactory(
		QVariant::Bool,
		qtnCreateFactory<QtnPropertyBoolCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::String,
		qtnCreateFactory<QtnPropertyQStringCallback>());
	qtnRegisterMetaPropertyFactory(QVariant::Double, createRealNumberProperty);
	qtnRegisterMetaPropertyFactory(
		QVariant::Int,
		qtnCreateFactory<QtnPropertyIntCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::UInt,
		qtnCreateFactory<QtnPropertyUIntCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Point,
		qtnCreateFactory<QtnPropertyQPointCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Rect,
		qtnCreateFactory<QtnPropertyQRectCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Size,
		qtnCreateFactory<QtnPropertyQSizeCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Color,
		qtnCreateFactory<QtnPropertyQColorCallback>());
	qtnRegisterMetaPropertyFactory(
		QVariant::Font,
		qtnCreateFactory<QtnPropertyQFontCallback>());
	return true;
}

static QMap<int, QtnMetaPropertyFactory_t> qtnFactoryMap;
static bool success = qtnRegisterDefaultMetaPropertyFactory();

bool qtnRegisterMetaPropertyFactory(
	int metaPropertyType, const
	QtnMetaPropertyFactory_t &factory, bool force)
{
	Q_ASSERT(factory);

	if (!force && qtnFactoryMap.contains(metaPropertyType))
		return false;

	qtnFactoryMap.insert(metaPropertyType, factory);
	return true;
}

QtnPropertyState qtnPropertyStateToAdd(const QMetaProperty &metaProperty)
{
	QtnPropertyState toAdd;

	if (!metaProperty.isDesignable())
		toAdd |= QtnPropertyStateInvisible;

	if (metaProperty.isConstant()
		|| (!metaProperty.isWritable() && !metaProperty.isResettable()))
	{
		toAdd |= QtnPropertyStateImmutable;
	}

	return toAdd;
}

void qtnUpdatePropertyState(
	QtnPropertyBase *property, const QMetaProperty &metaProperty)
{
	property->addState(qtnPropertyStateToAdd(metaProperty));
}

QtnProperty *qtnCreateQObjectProperty(
	QObject *object, const QMetaProperty &metaProperty,
	bool connect, const char *className)
{
	if (!object)
		return nullptr;

	auto it = qtnFactoryMap.find(metaProperty.type());
	if (it == qtnFactoryMap.end())
		it = qtnFactoryMap.find(metaProperty.userType());
	if (it == qtnFactoryMap.end())
		return nullptr;

	if (!metaProperty.isDesignable(object) || !metaProperty.isReadable())
		return nullptr;

	QtnProperty *property = it.value() (object, metaProperty);
	if (!property)
		return property;

	property->setName(
		(nullptr != className)
		? QCoreApplication::translate(className, metaProperty.name())
		: metaProperty.name());

	auto stateProvider = dynamic_cast<IQtnPropertyStateProvider *>(object);
	if (nullptr != stateProvider)
	{
		auto state = stateProvider->getPropertyState(metaProperty);
		property->setState(state);
	}

	qtnUpdatePropertyState(property, metaProperty);

	if (connect && metaProperty.hasNotifySignal())
	{
		auto connector = new QtnPropertyConnector(property);
		connector->connectProperty(object, metaProperty);
	}

	return property;
}

QtnProperty *qtnCreateQObjectProperty(
	QObject *object, const char *propertyName, bool connect)
{
	if (!object)
		return nullptr;

	const QMetaObject *metaObject = object->metaObject();
	int propertyIndex = -1;

	while (metaObject)
	{
		propertyIndex = object->metaObject()->indexOfProperty(propertyName);
		if (propertyIndex != -1)
			break;

		metaObject = metaObject->superClass();
	}

	if (!metaObject)
		return nullptr;

	if (propertyIndex == -1)
		return nullptr;

	Q_ASSERT(propertyIndex >= 0 && propertyIndex < metaObject->propertyCount());

	return qtnCreateQObjectProperty(
		object, metaObject->property(propertyIndex), connect,
		metaObject->className());
}

QtnPropertySet *qtnCreateQObjectPropertySet(QObject *object)
{
	if (!object)
		return nullptr;

	// collect property sets by object's classes
	QStringList classNames;
	std::map<QString, QtnPropertySet *> propertySetsByClass;

	auto metaObject = object->metaObject();
	while (nullptr != metaObject)
	{
		if (metaObject->propertyCount() > 0)
		{
			QList<QtnProperty *> properties;
			for (int propertyIndex = metaObject->propertyOffset(),
				 n = metaObject->propertyCount();
				 propertyIndex < n; ++propertyIndex)
			{
				auto metaProperty = metaObject->property(propertyIndex);
				auto property = qtnCreateQObjectProperty(
						object, metaProperty, true, metaObject->className());
				if (nullptr != property)
					properties.append(property);
			}

			if (!properties.isEmpty())
			{
				auto className = QCoreApplication::translate(
						"ClassName",
						metaObject->className());
				auto it = propertySetsByClass.find(className);

				QtnPropertySet *propertySetByClass;
				if (it != propertySetsByClass.end())
					propertySetByClass = it->second;
				else
				{
					propertySetByClass = new QtnPropertySet(nullptr);
					propertySetByClass->setName(className);
					propertySetsByClass[className] = propertySetByClass;

					classNames.push_back(className);
				}

				for (auto property : properties)
				{
					propertySetByClass->addChildProperty(property);
				}
			}
		}

		// move up in class hierarchy
		metaObject = metaObject->superClass();
	}

	if (propertySetsByClass.empty())
		return nullptr;

	// move collected property sets to object's property set
	auto propertySet = new QtnPropertySet(nullptr);
	propertySet->setName(object->objectName());

	for (auto &class_name : classNames)
	{
		propertySet->addChildProperty(propertySetsByClass[class_name]);
	}

	return propertySet;
}

QtnPropertySet *qtnCreateQObjectMultiPropertySet(
	const
	std::set<QObject *> &objects)
{
	if (objects.empty())
		return nullptr;

	auto result = new QtnPropertySet(nullptr);
	auto &subSets = result->childProperties();

	for (auto object : objects)
	{
		auto propertySet = qtnCreateQObjectPropertySet(object);
		if (nullptr == propertySet)
			continue;

		for (int i = propertySet->childProperties().count() - 1;
			 i >= 0; i--)
		{
			auto property = propertySet->childProperties().at(i);

			auto subSet = dynamic_cast<QtnPropertySet *>(property);
			Q_ASSERT(nullptr != subSet);

			auto it = std::find_if(
					subSets.begin(), subSets.end(),
					[subSet](const QtnPropertyBase *set) -> bool
			{
				return (subSet->name() == set->name());
			});

			QtnPropertySet *multiSet;
			if (it == subSets.end())
			{
				multiSet = new QtnPropertySet(nullptr);
				multiSet->setName(subSet->name());
				multiSet->setDescription(subSet->description());
				multiSet->setId(subSet->id());
				multiSet->setState(subSet->stateLocal());

				result->addChildProperty(multiSet, true, 0);
			} else
			{
				multiSet = dynamic_cast<QtnPropertySet *>(*it);
			}

			auto &ncp = multiSet->childProperties();
			for (auto subProp : subSet->childProperties())
			{
				auto propIt = std::find_if(
						ncp.begin(), ncp.end(),
						[subProp](QtnPropertyBase *p) -> bool
				{
					return p->name() == subProp->name();
				});

				QtnMultiProperty *multiProperty;
				if (propIt == ncp.end())
				{
					multiProperty = new QtnMultiProperty(subProp->metaObject());
					multiProperty->setName(subProp->name());
					multiProperty->setDescription(subProp->description());
					multiProperty->setId(subProp->id());

					multiSet->addChildProperty(multiProperty);
				} else
				{
					multiProperty = dynamic_cast<QtnMultiProperty *>(*propIt);
				}

				multiProperty->addProperty(
					dynamic_cast<QtnProperty *>(subProp));
			}
		}

		delete propertySet;
	}

	return result;
}

void qtnInitPercentSpinBoxDelegate(QtnPropertyDelegateInfo &delegate)
{
	delegate.name = "SpinBox";
	delegate.attributes["suffix"] = QLocale().percent();
}

void qtnInitDegreeSpinBoxDelegate(QtnPropertyDelegateInfo &delegate)
{
	delegate.name = "SpinBox";
	delegate.attributes["suffix"] = QString::fromUtf8("º");
}
<|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

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

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// C++ includes

// Local includes
#include "nonlinear_implicit_system.h"
#include "diff_solver.h"
#include "equation_systems.h"
#include "libmesh_logging.h"
#include "nonlinear_solver.h"
#include "sparse_matrix.h"

namespace libMesh
{

// ------------------------------------------------------------
// NonlinearImplicitSystem implementation
NonlinearImplicitSystem::NonlinearImplicitSystem (EquationSystems& es,
						  const std::string& name,
						  const unsigned int number) :

  Parent                    (es, name, number),
  nonlinear_solver          (NonlinearSolver<Number>::build(*this)),
  diff_solver               (NULL),
  _n_nonlinear_iterations   (0),
  _final_nonlinear_residual (1.e20)
{
  // Set default parameters
  // These were chosen to match the Petsc defaults
  es.parameters.set<Real>        ("linear solver tolerance") = 1e-5;
  es.parameters.set<Real>        ("linear solver minimum tolerance") = 1e-5;
  es.parameters.set<unsigned int>("linear solver maximum iterations") = 10000;

  es.parameters.set<unsigned int>("nonlinear solver maximum iterations") = 50;
  es.parameters.set<unsigned int>("nonlinear solver maximum function evaluations") = 10000;

  es.parameters.set<Real>("nonlinear solver absolute residual tolerance") = 1e-50;
  es.parameters.set<Real>("nonlinear solver relative residual tolerance") = 1e-8;
  es.parameters.set<Real>("nonlinear solver absolute step tolerance") = 1e-8;
  es.parameters.set<Real>("nonlinear solver relative step tolerance") = 1e-8;
}

NonlinearImplicitSystem::~NonlinearImplicitSystem ()
{
  // Clear data
  this->clear();
}

void NonlinearImplicitSystem::clear ()
{
  // clear the nonlinear solver
  nonlinear_solver->clear();

  // clear the parent data
  Parent::clear();
}

void NonlinearImplicitSystem::reinit ()
{
  // re-initialize the nonlinear solver interface
  nonlinear_solver->clear();

  if (diff_solver.get())
    diff_solver->reinit();

  // initialize parent data
  Parent::reinit();
}

void NonlinearImplicitSystem::set_solver_parameters ()
{
  // Get a reference to the EquationSystems
  const EquationSystems& es =
    this->get_equation_systems();

  // Get the user-specifiied nonlinear solver tolerances
  const unsigned int maxits =
    es.parameters.get<unsigned int>("nonlinear solver maximum iterations");

  const unsigned int maxfuncs =
    es.parameters.get<unsigned int>("nonlinear solver maximum function evaluations");

  const Real abs_resid_tol =
    es.parameters.get<Real>("nonlinear solver absolute residual tolerance");

  const Real rel_resid_tol =
    es.parameters.get<Real>("nonlinear solver relative residual tolerance");

  const Real abs_step_tol =
    es.parameters.get<Real>("nonlinear solver absolute step tolerance");

  const Real rel_step_tol =
    es.parameters.get<Real>("nonlinear solver relative step tolerance");

  // Get the user-specified linear solver toleranaces
  const unsigned int maxlinearits =
    es.parameters.get<unsigned int>("linear solver maximum iterations");

  const Real linear_tol =
    es.parameters.get<Real>("linear solver tolerance");

  const Real linear_min_tol =
    es.parameters.get<Real>("linear solver minimum tolerance");

  // Set all the parameters on the NonlinearSolver
  nonlinear_solver->max_nonlinear_iterations = maxits;
  nonlinear_solver->max_function_evaluations = maxfuncs;
  nonlinear_solver->absolute_residual_tolerance = abs_resid_tol;
  nonlinear_solver->relative_residual_tolerance = rel_resid_tol;
  nonlinear_solver->absolute_step_tolerance = abs_step_tol;
  nonlinear_solver->relative_step_tolerance = rel_step_tol;
  nonlinear_solver->max_linear_iterations = maxlinearits;
  nonlinear_solver->initial_linear_tolerance = linear_tol;
  nonlinear_solver->minimum_linear_tolerance = linear_min_tol;

  if (diff_solver.get())
    {
      diff_solver->max_nonlinear_iterations = maxits;
      diff_solver->absolute_residual_tolerance = abs_resid_tol;
      diff_solver->relative_residual_tolerance = rel_resid_tol;
      diff_solver->absolute_step_tolerance = abs_step_tol;
      diff_solver->relative_step_tolerance = rel_step_tol;
      diff_solver->max_linear_iterations = maxlinearits;
      diff_solver->initial_linear_tolerance = linear_tol;
      diff_solver->minimum_linear_tolerance = linear_min_tol;
    }
}

void NonlinearImplicitSystem::solve ()
{
  // Log how long the nonlinear solve takes.
  START_LOG("solve()", "System");

  this->set_solver_parameters();

  if (diff_solver.get())
    {
      diff_solver->solve();

      // Store the number of nonlinear iterations required to
      // solve and the final residual.
      _n_nonlinear_iterations   = diff_solver->total_outer_iterations();
      _final_nonlinear_residual = 0.; // FIXME - support this!
    }
  else
    {
      // Solve the nonlinear system.
      const std::pair<unsigned int, Real> rval =
        nonlinear_solver->solve (*matrix, *solution, *rhs,
			         nonlinear_solver->relative_residual_tolerance,
                                 nonlinear_solver->max_linear_iterations);

      // Store the number of nonlinear iterations required to
      // solve and the final residual.
      _n_nonlinear_iterations   = rval.first;
      _final_nonlinear_residual = rval.second;
    }

  // Stop logging the nonlinear solve
  STOP_LOG("solve()", "System");

  // Update the system after the solve
  this->update();
}

std::pair<unsigned int, Real> NonlinearImplicitSystem::get_linear_solve_parameters() const
{
  if (diff_solver.get())
    return std::make_pair(this->diff_solver->max_linear_iterations,
                        this->diff_solver->relative_residual_tolerance);
  return std::make_pair(this->nonlinear_solver->max_linear_iterations,
                        this->nonlinear_solver->relative_residual_tolerance);
}

void NonlinearImplicitSystem::assembly(bool get_residual,
				       bool get_jacobian)
{
  // Get current_local_solution in sync
  this->update();

  //-----------------------------------------------------------------------------
  // if the user has provided both function pointers and objects only the pointer
  // will be used, so catch that as an error
  if (nonlinear_solver->jacobian && nonlinear_solver->jacobian_object)
    {
      libMesh::err << "ERROR: cannot specifiy both a function and object to compute the Jacobian!" << std::endl;
      libmesh_error();
    }

  if (nonlinear_solver->residual && nonlinear_solver->residual_object)
    {
      libMesh::err << "ERROR: cannot specifiy both a function and object to compute the Residual!" << std::endl;
      libmesh_error();
    }

  if (nonlinear_solver->matvec && nonlinear_solver->residual_and_jacobian_object)
    {
      libMesh::err << "ERROR: cannot specifiy both a function and object to compute the combined Residual & Jacobian!" << std::endl;
      libmesh_error();
    }
  //-----------------------------------------------------------------------------

  if (get_jacobian)
    {
      if (nonlinear_solver->jacobian != NULL)
        nonlinear_solver->jacobian (*current_local_solution.get(), *matrix, *this);

      else if (nonlinear_solver->jacobian_object != NULL)
	nonlinear_solver->jacobian_object->jacobian (*current_local_solution.get(), *matrix, *this);

      else if (nonlinear_solver->matvec != NULL)
        nonlinear_solver->matvec (*current_local_solution.get(), get_residual?rhs:NULL, matrix, *this);

      else if (nonlinear_solver->residual_and_jacobian_object != NULL)
	nonlinear_solver->residual_and_jacobian_object->residual_and_jacobian (*current_local_solution.get(), get_residual?rhs:NULL, matrix, *this);

      else
	libmesh_error();
    }

  if (get_residual)
    {
      if (nonlinear_solver->residual != NULL)
        nonlinear_solver->residual (*current_local_solution.get(), *rhs, *this);

      else if (nonlinear_solver->residual_object != NULL)
	nonlinear_solver->residual_object->residual (*current_local_solution.get(), *rhs, *this);

      else if (nonlinear_solver->matvec != NULL)
        {
          // we might have already grabbed the residual and jacobian together
          if (!get_jacobian)
            nonlinear_solver->matvec (*current_local_solution.get(), rhs, NULL, *this);
        }

      else if (nonlinear_solver->residual_and_jacobian_object != NULL)
	{
          // we might have already grabbed the residual and jacobian together
          if (!get_jacobian)
	    nonlinear_solver->residual_and_jacobian_object->residual_and_jacobian (*current_local_solution.get(), rhs, NULL, *this);
	}

      else
        libmesh_error();
    }
  else
    libmesh_assert(get_jacobian);  // I can't believe you really wanted to assemble *nothing*
}

unsigned NonlinearImplicitSystem::get_current_nonlinear_iteration_number() const
{
  return nonlinear_solver->get_current_nonlinear_iteration_number();
}

} // namespace libMesh
<commit_msg>Avoid underflow with Real==float<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

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

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// C++ includes

// Local includes
#include "nonlinear_implicit_system.h"
#include "diff_solver.h"
#include "equation_systems.h"
#include "libmesh_logging.h"
#include "nonlinear_solver.h"
#include "sparse_matrix.h"

namespace libMesh
{

// ------------------------------------------------------------
// NonlinearImplicitSystem implementation
NonlinearImplicitSystem::NonlinearImplicitSystem (EquationSystems& es,
						  const std::string& name,
						  const unsigned int number) :

  Parent                    (es, name, number),
  nonlinear_solver          (NonlinearSolver<Number>::build(*this)),
  diff_solver               (NULL),
  _n_nonlinear_iterations   (0),
  _final_nonlinear_residual (1.e20)
{
  // Set default parameters
  // These were chosen to match the Petsc defaults
  es.parameters.set<Real>        ("linear solver tolerance") = 1e-5;
  es.parameters.set<Real>        ("linear solver minimum tolerance") = 1e-5;
  es.parameters.set<unsigned int>("linear solver maximum iterations") = 10000;

  es.parameters.set<unsigned int>("nonlinear solver maximum iterations") = 50;
  es.parameters.set<unsigned int>("nonlinear solver maximum function evaluations") = 10000;

  es.parameters.set<Real>("nonlinear solver absolute residual tolerance") = 1e-35;
  es.parameters.set<Real>("nonlinear solver relative residual tolerance") = 1e-8;
  es.parameters.set<Real>("nonlinear solver absolute step tolerance") = 1e-8;
  es.parameters.set<Real>("nonlinear solver relative step tolerance") = 1e-8;
}

NonlinearImplicitSystem::~NonlinearImplicitSystem ()
{
  // Clear data
  this->clear();
}

void NonlinearImplicitSystem::clear ()
{
  // clear the nonlinear solver
  nonlinear_solver->clear();

  // clear the parent data
  Parent::clear();
}

void NonlinearImplicitSystem::reinit ()
{
  // re-initialize the nonlinear solver interface
  nonlinear_solver->clear();

  if (diff_solver.get())
    diff_solver->reinit();

  // initialize parent data
  Parent::reinit();
}

void NonlinearImplicitSystem::set_solver_parameters ()
{
  // Get a reference to the EquationSystems
  const EquationSystems& es =
    this->get_equation_systems();

  // Get the user-specifiied nonlinear solver tolerances
  const unsigned int maxits =
    es.parameters.get<unsigned int>("nonlinear solver maximum iterations");

  const unsigned int maxfuncs =
    es.parameters.get<unsigned int>("nonlinear solver maximum function evaluations");

  const Real abs_resid_tol =
    es.parameters.get<Real>("nonlinear solver absolute residual tolerance");

  const Real rel_resid_tol =
    es.parameters.get<Real>("nonlinear solver relative residual tolerance");

  const Real abs_step_tol =
    es.parameters.get<Real>("nonlinear solver absolute step tolerance");

  const Real rel_step_tol =
    es.parameters.get<Real>("nonlinear solver relative step tolerance");

  // Get the user-specified linear solver toleranaces
  const unsigned int maxlinearits =
    es.parameters.get<unsigned int>("linear solver maximum iterations");

  const Real linear_tol =
    es.parameters.get<Real>("linear solver tolerance");

  const Real linear_min_tol =
    es.parameters.get<Real>("linear solver minimum tolerance");

  // Set all the parameters on the NonlinearSolver
  nonlinear_solver->max_nonlinear_iterations = maxits;
  nonlinear_solver->max_function_evaluations = maxfuncs;
  nonlinear_solver->absolute_residual_tolerance = abs_resid_tol;
  nonlinear_solver->relative_residual_tolerance = rel_resid_tol;
  nonlinear_solver->absolute_step_tolerance = abs_step_tol;
  nonlinear_solver->relative_step_tolerance = rel_step_tol;
  nonlinear_solver->max_linear_iterations = maxlinearits;
  nonlinear_solver->initial_linear_tolerance = linear_tol;
  nonlinear_solver->minimum_linear_tolerance = linear_min_tol;

  if (diff_solver.get())
    {
      diff_solver->max_nonlinear_iterations = maxits;
      diff_solver->absolute_residual_tolerance = abs_resid_tol;
      diff_solver->relative_residual_tolerance = rel_resid_tol;
      diff_solver->absolute_step_tolerance = abs_step_tol;
      diff_solver->relative_step_tolerance = rel_step_tol;
      diff_solver->max_linear_iterations = maxlinearits;
      diff_solver->initial_linear_tolerance = linear_tol;
      diff_solver->minimum_linear_tolerance = linear_min_tol;
    }
}

void NonlinearImplicitSystem::solve ()
{
  // Log how long the nonlinear solve takes.
  START_LOG("solve()", "System");

  this->set_solver_parameters();

  if (diff_solver.get())
    {
      diff_solver->solve();

      // Store the number of nonlinear iterations required to
      // solve and the final residual.
      _n_nonlinear_iterations   = diff_solver->total_outer_iterations();
      _final_nonlinear_residual = 0.; // FIXME - support this!
    }
  else
    {
      // Solve the nonlinear system.
      const std::pair<unsigned int, Real> rval =
        nonlinear_solver->solve (*matrix, *solution, *rhs,
			         nonlinear_solver->relative_residual_tolerance,
                                 nonlinear_solver->max_linear_iterations);

      // Store the number of nonlinear iterations required to
      // solve and the final residual.
      _n_nonlinear_iterations   = rval.first;
      _final_nonlinear_residual = rval.second;
    }

  // Stop logging the nonlinear solve
  STOP_LOG("solve()", "System");

  // Update the system after the solve
  this->update();
}

std::pair<unsigned int, Real> NonlinearImplicitSystem::get_linear_solve_parameters() const
{
  if (diff_solver.get())
    return std::make_pair(this->diff_solver->max_linear_iterations,
                        this->diff_solver->relative_residual_tolerance);
  return std::make_pair(this->nonlinear_solver->max_linear_iterations,
                        this->nonlinear_solver->relative_residual_tolerance);
}

void NonlinearImplicitSystem::assembly(bool get_residual,
				       bool get_jacobian)
{
  // Get current_local_solution in sync
  this->update();

  //-----------------------------------------------------------------------------
  // if the user has provided both function pointers and objects only the pointer
  // will be used, so catch that as an error
  if (nonlinear_solver->jacobian && nonlinear_solver->jacobian_object)
    {
      libMesh::err << "ERROR: cannot specifiy both a function and object to compute the Jacobian!" << std::endl;
      libmesh_error();
    }

  if (nonlinear_solver->residual && nonlinear_solver->residual_object)
    {
      libMesh::err << "ERROR: cannot specifiy both a function and object to compute the Residual!" << std::endl;
      libmesh_error();
    }

  if (nonlinear_solver->matvec && nonlinear_solver->residual_and_jacobian_object)
    {
      libMesh::err << "ERROR: cannot specifiy both a function and object to compute the combined Residual & Jacobian!" << std::endl;
      libmesh_error();
    }
  //-----------------------------------------------------------------------------

  if (get_jacobian)
    {
      if (nonlinear_solver->jacobian != NULL)
        nonlinear_solver->jacobian (*current_local_solution.get(), *matrix, *this);

      else if (nonlinear_solver->jacobian_object != NULL)
	nonlinear_solver->jacobian_object->jacobian (*current_local_solution.get(), *matrix, *this);

      else if (nonlinear_solver->matvec != NULL)
        nonlinear_solver->matvec (*current_local_solution.get(), get_residual?rhs:NULL, matrix, *this);

      else if (nonlinear_solver->residual_and_jacobian_object != NULL)
	nonlinear_solver->residual_and_jacobian_object->residual_and_jacobian (*current_local_solution.get(), get_residual?rhs:NULL, matrix, *this);

      else
	libmesh_error();
    }

  if (get_residual)
    {
      if (nonlinear_solver->residual != NULL)
        nonlinear_solver->residual (*current_local_solution.get(), *rhs, *this);

      else if (nonlinear_solver->residual_object != NULL)
	nonlinear_solver->residual_object->residual (*current_local_solution.get(), *rhs, *this);

      else if (nonlinear_solver->matvec != NULL)
        {
          // we might have already grabbed the residual and jacobian together
          if (!get_jacobian)
            nonlinear_solver->matvec (*current_local_solution.get(), rhs, NULL, *this);
        }

      else if (nonlinear_solver->residual_and_jacobian_object != NULL)
	{
          // we might have already grabbed the residual and jacobian together
          if (!get_jacobian)
	    nonlinear_solver->residual_and_jacobian_object->residual_and_jacobian (*current_local_solution.get(), rhs, NULL, *this);
	}

      else
        libmesh_error();
    }
  else
    libmesh_assert(get_jacobian);  // I can't believe you really wanted to assemble *nothing*
}

unsigned NonlinearImplicitSystem::get_current_nonlinear_iteration_number() const
{
  return nonlinear_solver->get_current_nonlinear_iteration_number();
}

} // namespace libMesh
<|endoftext|>


<commit_before>// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// 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.

#include "ExecutableMemory.hpp"

#include "Debug.hpp"

#if defined(_WIN32)
#	ifndef WIN32_LEAN_AND_MEAN
#		define WIN32_LEAN_AND_MEAN
#	endif
#	include <Windows.h>
#	include <intrin.h>
#elif defined(__Fuchsia__)
#	include <unistd.h>
#	include <zircon/process.h>
#	include <zircon/syscalls.h>
#else
#	include <errno.h>
#	include <sys/mman.h>
#	include <stdlib.h>
#	include <unistd.h>
#endif

#include <memory.h>

#undef allocate
#undef deallocate

#if(defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)) && !defined(__x86__)
#	define __x86__
#endif

#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)

namespace rr {
namespace {

struct Allocation
{
	//	size_t bytes;
	unsigned char *block;
};

void *allocateRaw(size_t bytes, size_t alignment)
{
	ASSERT((alignment & (alignment - 1)) == 0);  // Power of 2 alignment.

#if defined(REACTOR_ANONYMOUS_MMAP_NAME)
	if(alignment < sizeof(void *))
	{
		return malloc(bytes);
	}
	else
	{
		void *allocation;
		int result = posix_memalign(&allocation, alignment, bytes);
		if(result != 0)
		{
			errno = result;
			allocation = nullptr;
		}
		return allocation;
	}
#else
	unsigned char *block = new unsigned char[bytes + sizeof(Allocation) + alignment];
	unsigned char *aligned = nullptr;

	if(block)
	{
		aligned = (unsigned char *)((uintptr_t)(block + sizeof(Allocation) + alignment - 1) & -(intptr_t)alignment);
		Allocation *allocation = (Allocation *)(aligned - sizeof(Allocation));

		//	allocation->bytes = bytes;
		allocation->block = block;
	}

	return aligned;
#endif
}

#if defined(_WIN32)
DWORD permissionsToProtectMode(int permissions)
{
	switch(permissions)
	{
		case PERMISSION_READ:
			return PAGE_READONLY;
		case PERMISSION_EXECUTE:
			return PAGE_EXECUTE;
		case PERMISSION_READ | PERMISSION_WRITE:
			return PAGE_READWRITE;
		case PERMISSION_READ | PERMISSION_EXECUTE:
			return PAGE_EXECUTE_READ;
		case PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXECUTE:
			return PAGE_EXECUTE_READWRITE;
	}
	return PAGE_NOACCESS;
}
#endif

#if !defined(_WIN32) && !defined(__Fuchsia__)
int permissionsToMmapProt(int permissions)
{
	int result = 0;
	if(permissions & PERMISSION_READ)
	{
		result |= PROT_READ;
	}
	if(permissions & PERMISSION_WRITE)
	{
		result |= PROT_WRITE;
	}
	if(permissions & PERMISSION_EXECUTE)
	{
		result |= PROT_EXEC;
	}
	return result;
}
#endif  // !defined(_WIN32) && !defined(__Fuchsia__)

#if defined(REACTOR_ANONYMOUS_MMAP_NAME)
// Create a file descriptor for anonymous memory with the given
// name. Returns -1 on failure.
// TODO: remove once libc wrapper exists.
static int memfd_create(const char *name, unsigned int flags)
{
#	if __aarch64__
#		define __NR_memfd_create 279
#	elif __arm__
#		define __NR_memfd_create 279
#	elif __powerpc64__
#		define __NR_memfd_create 360
#	elif __i386__
#		define __NR_memfd_create 356
#	elif __x86_64__
#		define __NR_memfd_create 319
#	endif /* __NR_memfd_create__ */
#	ifdef __NR_memfd_create
	// In the event of no system call this returns -1 with errno set
	// as ENOSYS.
	return syscall(__NR_memfd_create, name, flags);
#	else
	return -1;
#	endif
}

// Returns a file descriptor for use with an anonymous mmap, if
// memfd_create fails, -1 is returned. Note, the mappings should be
// MAP_PRIVATE so that underlying pages aren't shared.
int anonymousFd()
{
	static int fd = memfd_create(MACRO_STRINGIFY(REACTOR_ANONYMOUS_MMAP_NAME), 0);
	return fd;
}

// Ensure there is enough space in the "anonymous" fd for length.
void ensureAnonFileSize(int anonFd, size_t length)
{
	static size_t fileSize = 0;
	if(length > fileSize)
	{
		ftruncate(anonFd, length);
		fileSize = length;
	}
}
#endif  // defined(REACTOR_ANONYMOUS_MMAP_NAME)

#if defined(__Fuchsia__)
zx_vm_option_t permissionsToZxVmOptions(int permissions)
{
	zx_vm_option_t result = 0;
	if(permissions & PERMISSION_READ)
	{
		result |= ZX_VM_PERM_READ;
	}
	if(permissions & PERMISSION_WRITE)
	{
		result |= ZX_VM_PERM_WRITE;
	}
	if(permissions & PERMISSION_EXECUTE)
	{
		result |= ZX_VM_PERM_EXECUTE;
	}
	return result;
}
#endif  // defined(__Fuchsia__)

}  // anonymous namespace

size_t memoryPageSize()
{
	static int pageSize = [] {
#if defined(_WIN32)
		SYSTEM_INFO systemInfo;
		GetSystemInfo(&systemInfo);
		return systemInfo.dwPageSize;
#else
		return sysconf(_SC_PAGESIZE);
#endif
	}();

	return pageSize;
}

void *allocate(size_t bytes, size_t alignment)
{
	void *memory = allocateRaw(bytes, alignment);

	if(memory)
	{
		memset(memory, 0, bytes);
	}

	return memory;
}

void deallocate(void *memory)
{
#if defined(REACTOR_ANONYMOUS_MMAP_NAME)
	free(memory);
#else
	if(memory)
	{
		unsigned char *aligned = (unsigned char *)memory;
		Allocation *allocation = (Allocation *)(aligned - sizeof(Allocation));

		delete[] allocation->block;
	}
#endif
}

// Rounds |x| up to a multiple of |m|, where |m| is a power of 2.
inline uintptr_t roundUp(uintptr_t x, uintptr_t m)
{
	ASSERT(m > 0 && (m & (m - 1)) == 0);  // |m| must be a power of 2.
	return (x + m - 1) & ~(m - 1);
}

void *allocateMemoryPages(size_t bytes, int permissions, bool need_exec)
{
	size_t pageSize = memoryPageSize();
	size_t length = roundUp(bytes, pageSize);
	void *mapping = nullptr;

#if defined(REACTOR_ANONYMOUS_MMAP_NAME)
	int flags = MAP_PRIVATE;

	// Try to name the memory region for the executable code,
	// to aid profilers.
	int anonFd = anonymousFd();
	if(anonFd == -1)
	{
		flags |= MAP_ANONYMOUS;
	}
	else
	{
		ensureAnonFileSize(anonFd, length);
	}

	mapping = mmap(
	    nullptr, length, permissionsToMmapProt(permissions), flags, anonFd, 0);

	if(mapping == MAP_FAILED)
	{
		mapping = nullptr;
	}
#elif defined(__Fuchsia__)
	zx_handle_t vmo;
	if(zx_vmo_create(length, 0, &vmo) != ZX_OK)
	{
		return nullptr;
	}
	if(need_exec &&
	   zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo) != ZX_OK)
	{
		return nullptr;
	}
	zx_vaddr_t reservation;
	zx_status_t status = zx_vmar_map(
	    zx_vmar_root_self(), permissionsToZxVmOptions(permissions), 0, vmo,
	    0, length, &reservation);
	zx_handle_close(vmo);
	if(status != ZX_OK)
	{
		return nullptr;
	}

	// zx_vmar_map() returns page-aligned address.
	ASSERT(roundUp(reservation, pageSize) == reservation);

	mapping = reinterpret_cast<void *>(reservation);
#elif defined(__APPLE__)
	int prot = permissionsToMmapProt(permissions);
	int flags = MAP_PRIVATE | MAP_ANONYMOUS;
	// On macOS 10.14 and higher, executables that are code signed with the
	// "runtime" option cannot execute writable memory by default. They can opt
	// into this capability by specifying the "com.apple.security.cs.allow-jit"
	// code signing entitlement and allocating the region with the MAP_JIT flag.
	mapping = mmap(nullptr, length, prot, flags | MAP_JIT, -1, 0);

	if(mapping == MAP_FAILED)
	{
		// Retry without MAP_JIT (for older macOS versions).
		mapping = mmap(nullptr, length, prot, flags, -1, 0);
	}

	if(mapping == MAP_FAILED)
	{
		mapping = nullptr;
	}
#else
	mapping = allocate(length, pageSize);
	protectMemoryPages(mapping, length, permissions);
#endif

	return mapping;
}

void protectMemoryPages(void *memory, size_t bytes, int permissions)
{
	if(bytes == 0)
	{
		return;
	}

	bytes = roundUp(bytes, memoryPageSize());

#if defined(_WIN32)
	unsigned long oldProtection;
	BOOL result =
	    VirtualProtect(memory, bytes, permissionsToProtectMode(permissions),
	                   &oldProtection);
	ASSERT(result);
#elif defined(__Fuchsia__)
	zx_status_t status = zx_vmar_protect(
	    zx_vmar_root_self(), permissionsToZxVmOptions(permissions),
	    reinterpret_cast<zx_vaddr_t>(memory), bytes);
	ASSERT(status == ZX_OK);
#else
	int result =
	    mprotect(memory, bytes, permissionsToMmapProt(permissions));
	ASSERT(result == 0);
#endif
}

void deallocateMemoryPages(void *memory, size_t bytes)
{
#if defined(_WIN32)
	unsigned long oldProtection;
	BOOL result =
	    VirtualProtect(memory, bytes, PAGE_READWRITE, &oldProtection);
	ASSERT(result);
	deallocate(memory);
#elif defined(__APPLE__) || defined(REACTOR_ANONYMOUS_MMAP_NAME)
	size_t pageSize = memoryPageSize();
	size_t length = (bytes + pageSize - 1) & ~(pageSize - 1);
	int result = munmap(memory, length);
	ASSERT(result == 0);
#elif defined(__Fuchsia__)
	size_t pageSize = memoryPageSize();
	size_t length = roundUp(bytes, pageSize);
	zx_status_t status = zx_vmar_unmap(
	    zx_vmar_root_self(), reinterpret_cast<zx_vaddr_t>(memory), length);
	ASSERT(status == ZX_OK);
#else
	int result = mprotect(memory, bytes, PROT_READ | PROT_WRITE);
	ASSERT(result == 0);
	deallocate(memory);
#endif
}

}  // namespace rr
<commit_msg>Only enable naming anonymous mmap on Linux<commit_after>// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// 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.

#include "ExecutableMemory.hpp"

#include "Debug.hpp"

#if defined(_WIN32)
#	ifndef WIN32_LEAN_AND_MEAN
#		define WIN32_LEAN_AND_MEAN
#	endif
#	include <Windows.h>
#	include <intrin.h>
#elif defined(__Fuchsia__)
#	include <unistd.h>
#	include <zircon/process.h>
#	include <zircon/syscalls.h>
#else
#	include <errno.h>
#	include <sys/mman.h>
#	include <stdlib.h>
#	include <unistd.h>
#endif

#include <memory.h>

#undef allocate
#undef deallocate

#if(defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)) && !defined(__x86__)
#	define __x86__
#endif

#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)

namespace rr {
namespace {

struct Allocation
{
	//	size_t bytes;
	unsigned char *block;
};

void *allocateRaw(size_t bytes, size_t alignment)
{
	ASSERT((alignment & (alignment - 1)) == 0);  // Power of 2 alignment.

#if defined(__linux__) && defined(REACTOR_ANONYMOUS_MMAP_NAME)
	if(alignment < sizeof(void *))
	{
		return malloc(bytes);
	}
	else
	{
		void *allocation;
		int result = posix_memalign(&allocation, alignment, bytes);
		if(result != 0)
		{
			errno = result;
			allocation = nullptr;
		}
		return allocation;
	}
#else
	unsigned char *block = new unsigned char[bytes + sizeof(Allocation) + alignment];
	unsigned char *aligned = nullptr;

	if(block)
	{
		aligned = (unsigned char *)((uintptr_t)(block + sizeof(Allocation) + alignment - 1) & -(intptr_t)alignment);
		Allocation *allocation = (Allocation *)(aligned - sizeof(Allocation));

		//	allocation->bytes = bytes;
		allocation->block = block;
	}

	return aligned;
#endif
}

#if defined(_WIN32)
DWORD permissionsToProtectMode(int permissions)
{
	switch(permissions)
	{
		case PERMISSION_READ:
			return PAGE_READONLY;
		case PERMISSION_EXECUTE:
			return PAGE_EXECUTE;
		case PERMISSION_READ | PERMISSION_WRITE:
			return PAGE_READWRITE;
		case PERMISSION_READ | PERMISSION_EXECUTE:
			return PAGE_EXECUTE_READ;
		case PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXECUTE:
			return PAGE_EXECUTE_READWRITE;
	}
	return PAGE_NOACCESS;
}
#endif

#if !defined(_WIN32) && !defined(__Fuchsia__)
int permissionsToMmapProt(int permissions)
{
	int result = 0;
	if(permissions & PERMISSION_READ)
	{
		result |= PROT_READ;
	}
	if(permissions & PERMISSION_WRITE)
	{
		result |= PROT_WRITE;
	}
	if(permissions & PERMISSION_EXECUTE)
	{
		result |= PROT_EXEC;
	}
	return result;
}
#endif  // !defined(_WIN32) && !defined(__Fuchsia__)

#if defined(__linux__) && defined(REACTOR_ANONYMOUS_MMAP_NAME)
// Create a file descriptor for anonymous memory with the given
// name. Returns -1 on failure.
// TODO: remove once libc wrapper exists.
static int memfd_create(const char *name, unsigned int flags)
{
#	if __aarch64__
#		define __NR_memfd_create 279
#	elif __arm__
#		define __NR_memfd_create 279
#	elif __powerpc64__
#		define __NR_memfd_create 360
#	elif __i386__
#		define __NR_memfd_create 356
#	elif __x86_64__
#		define __NR_memfd_create 319
#	endif /* __NR_memfd_create__ */
#	ifdef __NR_memfd_create
	// In the event of no system call this returns -1 with errno set
	// as ENOSYS.
	return syscall(__NR_memfd_create, name, flags);
#	else
	return -1;
#	endif
}

// Returns a file descriptor for use with an anonymous mmap, if
// memfd_create fails, -1 is returned. Note, the mappings should be
// MAP_PRIVATE so that underlying pages aren't shared.
int anonymousFd()
{
	static int fd = memfd_create(MACRO_STRINGIFY(REACTOR_ANONYMOUS_MMAP_NAME), 0);
	return fd;
}

// Ensure there is enough space in the "anonymous" fd for length.
void ensureAnonFileSize(int anonFd, size_t length)
{
	static size_t fileSize = 0;
	if(length > fileSize)
	{
		ftruncate(anonFd, length);
		fileSize = length;
	}
}
#endif  // defined(__linux__) && defined(REACTOR_ANONYMOUS_MMAP_NAME)

#if defined(__Fuchsia__)
zx_vm_option_t permissionsToZxVmOptions(int permissions)
{
	zx_vm_option_t result = 0;
	if(permissions & PERMISSION_READ)
	{
		result |= ZX_VM_PERM_READ;
	}
	if(permissions & PERMISSION_WRITE)
	{
		result |= ZX_VM_PERM_WRITE;
	}
	if(permissions & PERMISSION_EXECUTE)
	{
		result |= ZX_VM_PERM_EXECUTE;
	}
	return result;
}
#endif  // defined(__Fuchsia__)

}  // anonymous namespace

size_t memoryPageSize()
{
	static int pageSize = [] {
#if defined(_WIN32)
		SYSTEM_INFO systemInfo;
		GetSystemInfo(&systemInfo);
		return systemInfo.dwPageSize;
#else
		return sysconf(_SC_PAGESIZE);
#endif
	}();

	return pageSize;
}

void *allocate(size_t bytes, size_t alignment)
{
	void *memory = allocateRaw(bytes, alignment);

	if(memory)
	{
		memset(memory, 0, bytes);
	}

	return memory;
}

void deallocate(void *memory)
{
#if defined(__linux__) && defined(REACTOR_ANONYMOUS_MMAP_NAME)
	free(memory);
#else
	if(memory)
	{
		unsigned char *aligned = (unsigned char *)memory;
		Allocation *allocation = (Allocation *)(aligned - sizeof(Allocation));

		delete[] allocation->block;
	}
#endif
}

// Rounds |x| up to a multiple of |m|, where |m| is a power of 2.
inline uintptr_t roundUp(uintptr_t x, uintptr_t m)
{
	ASSERT(m > 0 && (m & (m - 1)) == 0);  // |m| must be a power of 2.
	return (x + m - 1) & ~(m - 1);
}

void *allocateMemoryPages(size_t bytes, int permissions, bool need_exec)
{
	size_t pageSize = memoryPageSize();
	size_t length = roundUp(bytes, pageSize);
	void *mapping = nullptr;

#if defined(__linux__) && defined(REACTOR_ANONYMOUS_MMAP_NAME)
	int flags = MAP_PRIVATE;

	// Try to name the memory region for the executable code,
	// to aid profilers.
	int anonFd = anonymousFd();
	if(anonFd == -1)
	{
		flags |= MAP_ANONYMOUS;
	}
	else
	{
		ensureAnonFileSize(anonFd, length);
	}

	mapping = mmap(
	    nullptr, length, permissionsToMmapProt(permissions), flags, anonFd, 0);

	if(mapping == MAP_FAILED)
	{
		mapping = nullptr;
	}
#elif defined(__Fuchsia__)
	zx_handle_t vmo;
	if(zx_vmo_create(length, 0, &vmo) != ZX_OK)
	{
		return nullptr;
	}
	if(need_exec &&
	   zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo) != ZX_OK)
	{
		return nullptr;
	}
	zx_vaddr_t reservation;
	zx_status_t status = zx_vmar_map(
	    zx_vmar_root_self(), permissionsToZxVmOptions(permissions), 0, vmo,
	    0, length, &reservation);
	zx_handle_close(vmo);
	if(status != ZX_OK)
	{
		return nullptr;
	}

	// zx_vmar_map() returns page-aligned address.
	ASSERT(roundUp(reservation, pageSize) == reservation);

	mapping = reinterpret_cast<void *>(reservation);
#elif defined(__APPLE__)
	int prot = permissionsToMmapProt(permissions);
	int flags = MAP_PRIVATE | MAP_ANONYMOUS;
	// On macOS 10.14 and higher, executables that are code signed with the
	// "runtime" option cannot execute writable memory by default. They can opt
	// into this capability by specifying the "com.apple.security.cs.allow-jit"
	// code signing entitlement and allocating the region with the MAP_JIT flag.
	mapping = mmap(nullptr, length, prot, flags | MAP_JIT, -1, 0);

	if(mapping == MAP_FAILED)
	{
		// Retry without MAP_JIT (for older macOS versions).
		mapping = mmap(nullptr, length, prot, flags, -1, 0);
	}

	if(mapping == MAP_FAILED)
	{
		mapping = nullptr;
	}
#else
	mapping = allocate(length, pageSize);
	protectMemoryPages(mapping, length, permissions);
#endif

	return mapping;
}

void protectMemoryPages(void *memory, size_t bytes, int permissions)
{
	if(bytes == 0)
	{
		return;
	}

	bytes = roundUp(bytes, memoryPageSize());

#if defined(_WIN32)
	unsigned long oldProtection;
	BOOL result =
	    VirtualProtect(memory, bytes, permissionsToProtectMode(permissions),
	                   &oldProtection);
	ASSERT(result);
#elif defined(__Fuchsia__)
	zx_status_t status = zx_vmar_protect(
	    zx_vmar_root_self(), permissionsToZxVmOptions(permissions),
	    reinterpret_cast<zx_vaddr_t>(memory), bytes);
	ASSERT(status == ZX_OK);
#else
	int result =
	    mprotect(memory, bytes, permissionsToMmapProt(permissions));
	ASSERT(result == 0);
#endif
}

void deallocateMemoryPages(void *memory, size_t bytes)
{
#if defined(_WIN32)
	unsigned long oldProtection;
	BOOL result =
	    VirtualProtect(memory, bytes, PAGE_READWRITE, &oldProtection);
	ASSERT(result);
	deallocate(memory);
#elif defined(__APPLE__) || (defined(__linux__) && defined(REACTOR_ANONYMOUS_MMAP_NAME))
	size_t pageSize = memoryPageSize();
	size_t length = (bytes + pageSize - 1) & ~(pageSize - 1);
	int result = munmap(memory, length);
	ASSERT(result == 0);
#elif defined(__Fuchsia__)
	size_t pageSize = memoryPageSize();
	size_t length = roundUp(bytes, pageSize);
	zx_status_t status = zx_vmar_unmap(
	    zx_vmar_root_self(), reinterpret_cast<zx_vaddr_t>(memory), length);
	ASSERT(status == ZX_OK);
#else
	int result = mprotect(memory, bytes, PROT_READ | PROT_WRITE);
	ASSERT(result == 0);
	deallocate(memory);
#endif
}

}  // namespace rr
<|endoftext|>


<commit_before>/**
 ** \file libport/perror.cc
 ** \brief perror: implements file libport/perror.hh
 */

#include <cstdio>
#include <iostream>

#include "libport/cstring"

namespace libport
{

  void
  perror (const char* s)
  {
#ifndef WIN32
    ::perror(s);
#else
    int errnum;
    const char* errstring;
    const char* colon;

    errnum = WSAGetLastError();
    errstring = strerror(errnum);

    if (s == NULL || *s == '\0')
      s = colon = "";
    else
      colon = ": ";

    std::cerr << s << colon << errstring << std::endl;
#endif
  }

}
<commit_msg>Add missing header for windows.<commit_after>/**
 ** \file libport/perror.cc
 ** \brief perror: implements file libport/perror.hh
 */

#include <cstdio>
#include <iostream>

#include "libport/windows.hh"
#include "libport/cstring"

namespace libport
{

  void
  perror (const char* s)
  {
#ifndef WIN32
    ::perror(s);
#else
    int errnum;
    const char* errstring;
    const char* colon;

    errnum = WSAGetLastError();
    errstring = strerror(errnum);

    if (s == NULL || *s == '\0')
      s = colon = "";
    else
      colon = ": ";

    std::cerr << s << colon << errstring << std::endl;
#endif
  }

}
<|endoftext|>


<commit_before>// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu_surface_gl.h"

#include "flutter/fml/arraysize.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/common/persistent_cache.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrContextOptions.h"
#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"

// These are common defines present on all OpenGL headers. However, we don't
// want to perform GL header reasolution on each platform we support. So just
// define these upfront. It is unlikely we will need more. But, if we do, we can
// add the same here.
#define GPU_GL_RGBA8 0x8058
#define GPU_GL_RGBA4 0x8056
#define GPU_GL_RGB565 0x8D62
#define GPU_GL_VERSION 0x1F02

namespace shell {

// Default maximum number of budgeted resources in the cache.
static const int kGrCacheMaxCount = 8192;

// Default maximum number of bytes of GPU memory of budgeted resources in the
// cache.
static const size_t kGrCacheMaxByteSize = 512 * (1 << 20);

// Version string prefix that identifies an OpenGL ES implementation.
static const char kGLESVersionPrefix[] = "OpenGL ES";

GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate)
    : delegate_(delegate), weak_factory_(this) {
  if (!delegate_->GLContextMakeCurrent()) {
    FML_LOG(ERROR)
        << "Could not make the context current to setup the gr context.";
    return;
  }

  proc_resolver_ = delegate_->GetGLProcResolver();

  GrContextOptions options;

  options.fPersistentCache = PersistentCache::GetCacheForProcess();

  options.fAvoidStencilBuffers = true;

  // To get video playback on the widest range of devices, we limit Skia to
  // ES2 shading language when the ES3 external image extension is missing.
  options.fPreferExternalImagesOverES3 = true;

  sk_sp<const GrGLInterface> interface;

  if (proc_resolver_ == nullptr) {
    interface = GrGLMakeNativeInterface();
  } else {
    auto gl_get_proc = [](void* context,
                          const char gl_proc_name[]) -> GrGLFuncPtr {
      return reinterpret_cast<GrGLFuncPtr>(
          reinterpret_cast<GPUSurfaceGL*>(context)->proc_resolver_(
              gl_proc_name));
    };

    if (IsProcResolverOpenGLES()) {
      interface = GrGLMakeAssembledGLESInterface(this, gl_get_proc);
    } else {
      interface = GrGLMakeAssembledGLInterface(this, gl_get_proc);
    }
  }

  auto context = GrContext::MakeGL(interface, options);

  if (context == nullptr) {
    FML_LOG(ERROR) << "Failed to setup Skia Gr context.";
    return;
  }

  context_ = std::move(context);

  context_->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);

  delegate_->GLContextClearCurrent();

  valid_ = true;
}

GPUSurfaceGL::~GPUSurfaceGL() {
  if (!valid_) {
    return;
  }

  if (!delegate_->GLContextMakeCurrent()) {
    FML_LOG(ERROR) << "Could not make the context current to destroy the "
                      "GrContext resources.";
    return;
  }

  onscreen_surface_ = nullptr;
  context_->releaseResourcesAndAbandonContext();
  context_ = nullptr;

  delegate_->GLContextClearCurrent();
}

bool GPUSurfaceGL::IsProcResolverOpenGLES() {
  using GLGetStringProc = const char* (*)(uint32_t);
  GLGetStringProc gl_get_string =
      reinterpret_cast<GLGetStringProc>(proc_resolver_("glGetString"));
  FML_CHECK(gl_get_string)
      << "The GL proc resolver could not resolve glGetString";
  const char* gl_version_string = gl_get_string(GPU_GL_VERSION);
  FML_CHECK(gl_version_string)
      << "The GL proc resolver's glGetString(GL_VERSION) failed";

  return strncmp(gl_version_string, kGLESVersionPrefix,
                 strlen(kGLESVersionPrefix)) == 0;
}

// |shell::Surface|
bool GPUSurfaceGL::IsValid() {
  return valid_;
}

static SkColorType FirstSupportedColorType(GrContext* context,
                                           GrGLenum* format) {
#define RETURN_IF_RENDERABLE(x, y)                 \
  if (context->colorTypeSupportedAsSurface((x))) { \
    *format = (y);                                 \
    return (x);                                    \
  }
  RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GPU_GL_RGBA8);
  RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GPU_GL_RGBA4);
  RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GPU_GL_RGB565);
  return kUnknown_SkColorType;
}

static sk_sp<SkSurface> WrapOnscreenSurface(GrContext* context,
                                            const SkISize& size,
                                            intptr_t fbo) {
  GrGLenum format;
  const SkColorType color_type = FirstSupportedColorType(context, &format);

  GrGLFramebufferInfo framebuffer_info = {};
  framebuffer_info.fFBOID = static_cast<GrGLuint>(fbo);
  framebuffer_info.fFormat = format;

  GrBackendRenderTarget render_target(size.width(),     // width
                                      size.height(),    // height
                                      0,                // sample count
                                      0,                // stencil bits (TODO)
                                      framebuffer_info  // framebuffer info
  );

  sk_sp<SkColorSpace> colorspace = nullptr;

  SkSurfaceProps surface_props(
      SkSurfaceProps::InitType::kLegacyFontHost_InitType);

  return SkSurface::MakeFromBackendRenderTarget(
      context,                                       // gr context
      render_target,                                 // render target
      GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,  // origin
      color_type,                                    // color type
      colorspace,                                    // colorspace
      &surface_props                                 // surface properties
  );
}

static sk_sp<SkSurface> CreateOffscreenSurface(GrContext* context,
                                               const SkISize& size) {
  const SkImageInfo image_info =
      SkImageInfo::MakeN32(size.fWidth, size.fHeight, kOpaque_SkAlphaType);

  const SkSurfaceProps surface_props(
      SkSurfaceProps::InitType::kLegacyFontHost_InitType);

  return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info, 0,
                                     kBottomLeft_GrSurfaceOrigin,
                                     &surface_props);
}

bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
  if (onscreen_surface_ != nullptr &&
      size == SkISize::Make(onscreen_surface_->width(),
                            onscreen_surface_->height())) {
    // Surface size appears unchanged. So bail.
    return true;
  }

  // We need to do some updates.
  TRACE_EVENT0("flutter", "UpdateSurfacesSize");

  // Either way, we need to get rid of previous surface.
  onscreen_surface_ = nullptr;
  offscreen_surface_ = nullptr;

  if (size.isEmpty()) {
    FML_LOG(ERROR) << "Cannot create surfaces of empty size.";
    return false;
  }

  sk_sp<SkSurface> onscreen_surface, offscreen_surface;

  onscreen_surface =
      WrapOnscreenSurface(context_.get(),            // GL context
                          size,                      // root surface size
                          delegate_->GLContextFBO()  // window FBO ID
      );

  if (onscreen_surface == nullptr) {
    // If the onscreen surface could not be wrapped. There is absolutely no
    // point in moving forward.
    FML_LOG(ERROR) << "Could not wrap onscreen surface.";
    return false;
  }

  if (delegate_->UseOffscreenSurface()) {
    offscreen_surface = CreateOffscreenSurface(context_.get(), size);
    if (offscreen_surface == nullptr) {
      FML_LOG(ERROR) << "Could not create offscreen surface.";
      return false;
    }
  }

  onscreen_surface_ = std::move(onscreen_surface);
  offscreen_surface_ = std::move(offscreen_surface);

  return true;
}

// |shell::Surface|
SkMatrix GPUSurfaceGL::GetRootTransformation() const {
  return delegate_->GLContextSurfaceTransformation();
}

// |shell::Surface|
std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
  if (delegate_ == nullptr) {
    return nullptr;
  }

  if (!delegate_->GLContextMakeCurrent()) {
    FML_LOG(ERROR)
        << "Could not make the context current to acquire the frame.";
    return nullptr;
  }

  const auto root_surface_transformation = GetRootTransformation();

  sk_sp<SkSurface> surface =
      AcquireRenderSurface(size, root_surface_transformation);

  if (surface == nullptr) {
    return nullptr;
  }

  surface->getCanvas()->setMatrix(root_surface_transformation);

  SurfaceFrame::SubmitCallback submit_callback =
      [weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame,
                                          SkCanvas* canvas) {
        return weak ? weak->PresentSurface(canvas) : false;
      };

  return std::make_unique<SurfaceFrame>(surface, submit_callback);
}

bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
  if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
    return false;
  }

  if (offscreen_surface_ != nullptr) {
    TRACE_EVENT0("flutter", "CopyTextureOnscreen");
    SkPaint paint;
    onscreen_surface_->getCanvas()->drawImage(
        offscreen_surface_->makeImageSnapshot(), 0, 0, &paint);
  }

  {
    TRACE_EVENT0("flutter", "SkCanvas::Flush");
    onscreen_surface_->getCanvas()->flush();
  }

  if (!delegate_->GLContextPresent()) {
    return false;
  }

  if (delegate_->GLContextFBOResetAfterPresent()) {
    auto current_size =
        SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height());

    // The FBO has changed, ask the delegate for the new FBO and do a surface
    // re-wrap.
    auto new_onscreen_surface =
        WrapOnscreenSurface(context_.get(),            // GL context
                            current_size,              // root surface size
                            delegate_->GLContextFBO()  // window FBO ID
        );

    if (!new_onscreen_surface) {
      return false;
    }

    onscreen_surface_ = std::move(new_onscreen_surface);
  }

  return true;
}

sk_sp<SkSurface> GPUSurfaceGL::AcquireRenderSurface(
    const SkISize& untransformed_size,
    const SkMatrix& root_surface_transformation) {
  const auto transformed_rect = root_surface_transformation.mapRect(
      SkRect::MakeWH(untransformed_size.width(), untransformed_size.height()));

  const auto transformed_size =
      SkISize::Make(transformed_rect.width(), transformed_rect.height());

  if (!CreateOrUpdateSurfaces(transformed_size)) {
    return nullptr;
  }

  return offscreen_surface_ != nullptr ? offscreen_surface_ : onscreen_surface_;
}

// |shell::Surface|
GrContext* GPUSurfaceGL::GetContext() {
  return context_.get();
}

// |shell::Surface|
flow::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() {
  return delegate_->GetExternalViewEmbedder();
}

}  // namespace shell
<commit_msg>Clear the on-screen surface every frame. (#6753)<commit_after>// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu_surface_gl.h"

#include "flutter/fml/arraysize.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/common/persistent_cache.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrContextOptions.h"
#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"

// These are common defines present on all OpenGL headers. However, we don't
// want to perform GL header reasolution on each platform we support. So just
// define these upfront. It is unlikely we will need more. But, if we do, we can
// add the same here.
#define GPU_GL_RGBA8 0x8058
#define GPU_GL_RGBA4 0x8056
#define GPU_GL_RGB565 0x8D62
#define GPU_GL_VERSION 0x1F02

namespace shell {

// Default maximum number of budgeted resources in the cache.
static const int kGrCacheMaxCount = 8192;

// Default maximum number of bytes of GPU memory of budgeted resources in the
// cache.
static const size_t kGrCacheMaxByteSize = 512 * (1 << 20);

// Version string prefix that identifies an OpenGL ES implementation.
static const char kGLESVersionPrefix[] = "OpenGL ES";

GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate)
    : delegate_(delegate), weak_factory_(this) {
  if (!delegate_->GLContextMakeCurrent()) {
    FML_LOG(ERROR)
        << "Could not make the context current to setup the gr context.";
    return;
  }

  proc_resolver_ = delegate_->GetGLProcResolver();

  GrContextOptions options;

  options.fPersistentCache = PersistentCache::GetCacheForProcess();

  options.fAvoidStencilBuffers = true;

  // To get video playback on the widest range of devices, we limit Skia to
  // ES2 shading language when the ES3 external image extension is missing.
  options.fPreferExternalImagesOverES3 = true;

  sk_sp<const GrGLInterface> interface;

  if (proc_resolver_ == nullptr) {
    interface = GrGLMakeNativeInterface();
  } else {
    auto gl_get_proc = [](void* context,
                          const char gl_proc_name[]) -> GrGLFuncPtr {
      return reinterpret_cast<GrGLFuncPtr>(
          reinterpret_cast<GPUSurfaceGL*>(context)->proc_resolver_(
              gl_proc_name));
    };

    if (IsProcResolverOpenGLES()) {
      interface = GrGLMakeAssembledGLESInterface(this, gl_get_proc);
    } else {
      interface = GrGLMakeAssembledGLInterface(this, gl_get_proc);
    }
  }

  auto context = GrContext::MakeGL(interface, options);

  if (context == nullptr) {
    FML_LOG(ERROR) << "Failed to setup Skia Gr context.";
    return;
  }

  context_ = std::move(context);

  context_->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);

  delegate_->GLContextClearCurrent();

  valid_ = true;
}

GPUSurfaceGL::~GPUSurfaceGL() {
  if (!valid_) {
    return;
  }

  if (!delegate_->GLContextMakeCurrent()) {
    FML_LOG(ERROR) << "Could not make the context current to destroy the "
                      "GrContext resources.";
    return;
  }

  onscreen_surface_ = nullptr;
  context_->releaseResourcesAndAbandonContext();
  context_ = nullptr;

  delegate_->GLContextClearCurrent();
}

bool GPUSurfaceGL::IsProcResolverOpenGLES() {
  using GLGetStringProc = const char* (*)(uint32_t);
  GLGetStringProc gl_get_string =
      reinterpret_cast<GLGetStringProc>(proc_resolver_("glGetString"));
  FML_CHECK(gl_get_string)
      << "The GL proc resolver could not resolve glGetString";
  const char* gl_version_string = gl_get_string(GPU_GL_VERSION);
  FML_CHECK(gl_version_string)
      << "The GL proc resolver's glGetString(GL_VERSION) failed";

  return strncmp(gl_version_string, kGLESVersionPrefix,
                 strlen(kGLESVersionPrefix)) == 0;
}

// |shell::Surface|
bool GPUSurfaceGL::IsValid() {
  return valid_;
}

static SkColorType FirstSupportedColorType(GrContext* context,
                                           GrGLenum* format) {
#define RETURN_IF_RENDERABLE(x, y)                 \
  if (context->colorTypeSupportedAsSurface((x))) { \
    *format = (y);                                 \
    return (x);                                    \
  }
  RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GPU_GL_RGBA8);
  RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GPU_GL_RGBA4);
  RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GPU_GL_RGB565);
  return kUnknown_SkColorType;
}

static sk_sp<SkSurface> WrapOnscreenSurface(GrContext* context,
                                            const SkISize& size,
                                            intptr_t fbo) {
  GrGLenum format;
  const SkColorType color_type = FirstSupportedColorType(context, &format);

  GrGLFramebufferInfo framebuffer_info = {};
  framebuffer_info.fFBOID = static_cast<GrGLuint>(fbo);
  framebuffer_info.fFormat = format;

  GrBackendRenderTarget render_target(size.width(),     // width
                                      size.height(),    // height
                                      0,                // sample count
                                      0,                // stencil bits (TODO)
                                      framebuffer_info  // framebuffer info
  );

  sk_sp<SkColorSpace> colorspace = nullptr;

  SkSurfaceProps surface_props(
      SkSurfaceProps::InitType::kLegacyFontHost_InitType);

  return SkSurface::MakeFromBackendRenderTarget(
      context,                                       // gr context
      render_target,                                 // render target
      GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,  // origin
      color_type,                                    // color type
      colorspace,                                    // colorspace
      &surface_props                                 // surface properties
  );
}

static sk_sp<SkSurface> CreateOffscreenSurface(GrContext* context,
                                               const SkISize& size) {
  const SkImageInfo image_info =
      SkImageInfo::MakeN32(size.fWidth, size.fHeight, kOpaque_SkAlphaType);

  const SkSurfaceProps surface_props(
      SkSurfaceProps::InitType::kLegacyFontHost_InitType);

  return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info, 0,
                                     kBottomLeft_GrSurfaceOrigin,
                                     &surface_props);
}

bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
  if (onscreen_surface_ != nullptr &&
      size == SkISize::Make(onscreen_surface_->width(),
                            onscreen_surface_->height())) {
    // Surface size appears unchanged. So bail.
    return true;
  }

  // We need to do some updates.
  TRACE_EVENT0("flutter", "UpdateSurfacesSize");

  // Either way, we need to get rid of previous surface.
  onscreen_surface_ = nullptr;
  offscreen_surface_ = nullptr;

  if (size.isEmpty()) {
    FML_LOG(ERROR) << "Cannot create surfaces of empty size.";
    return false;
  }

  sk_sp<SkSurface> onscreen_surface, offscreen_surface;

  onscreen_surface =
      WrapOnscreenSurface(context_.get(),            // GL context
                          size,                      // root surface size
                          delegate_->GLContextFBO()  // window FBO ID
      );

  if (onscreen_surface == nullptr) {
    // If the onscreen surface could not be wrapped. There is absolutely no
    // point in moving forward.
    FML_LOG(ERROR) << "Could not wrap onscreen surface.";
    return false;
  }

  if (delegate_->UseOffscreenSurface()) {
    offscreen_surface = CreateOffscreenSurface(context_.get(), size);
    if (offscreen_surface == nullptr) {
      FML_LOG(ERROR) << "Could not create offscreen surface.";
      return false;
    }
  }

  onscreen_surface_ = std::move(onscreen_surface);
  offscreen_surface_ = std::move(offscreen_surface);

  return true;
}

// |shell::Surface|
SkMatrix GPUSurfaceGL::GetRootTransformation() const {
  return delegate_->GLContextSurfaceTransformation();
}

// |shell::Surface|
std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
  if (delegate_ == nullptr) {
    return nullptr;
  }

  if (!delegate_->GLContextMakeCurrent()) {
    FML_LOG(ERROR)
        << "Could not make the context current to acquire the frame.";
    return nullptr;
  }

  const auto root_surface_transformation = GetRootTransformation();

  sk_sp<SkSurface> surface =
      AcquireRenderSurface(size, root_surface_transformation);

  if (surface == nullptr) {
    return nullptr;
  }

  surface->getCanvas()->setMatrix(root_surface_transformation);

  SurfaceFrame::SubmitCallback submit_callback =
      [weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame,
                                          SkCanvas* canvas) {
        return weak ? weak->PresentSurface(canvas) : false;
      };

  return std::make_unique<SurfaceFrame>(surface, submit_callback);
}

bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
  if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
    return false;
  }

  if (offscreen_surface_ != nullptr) {
    TRACE_EVENT0("flutter", "CopyTextureOnscreen");
    SkPaint paint;
    SkCanvas* onscreen_canvas = onscreen_surface_->getCanvas();
    onscreen_canvas->clear(SK_ColorTRANSPARENT);
    onscreen_canvas->drawImage(offscreen_surface_->makeImageSnapshot(), 0, 0,
                               &paint);
  }

  {
    TRACE_EVENT0("flutter", "SkCanvas::Flush");
    onscreen_surface_->getCanvas()->flush();
  }

  if (!delegate_->GLContextPresent()) {
    return false;
  }

  if (delegate_->GLContextFBOResetAfterPresent()) {
    auto current_size =
        SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height());

    // The FBO has changed, ask the delegate for the new FBO and do a surface
    // re-wrap.
    auto new_onscreen_surface =
        WrapOnscreenSurface(context_.get(),            // GL context
                            current_size,              // root surface size
                            delegate_->GLContextFBO()  // window FBO ID
        );

    if (!new_onscreen_surface) {
      return false;
    }

    onscreen_surface_ = std::move(new_onscreen_surface);
  }

  return true;
}

sk_sp<SkSurface> GPUSurfaceGL::AcquireRenderSurface(
    const SkISize& untransformed_size,
    const SkMatrix& root_surface_transformation) {
  const auto transformed_rect = root_surface_transformation.mapRect(
      SkRect::MakeWH(untransformed_size.width(), untransformed_size.height()));

  const auto transformed_size =
      SkISize::Make(transformed_rect.width(), transformed_rect.height());

  if (!CreateOrUpdateSurfaces(transformed_size)) {
    return nullptr;
  }

  return offscreen_surface_ != nullptr ? offscreen_surface_ : onscreen_surface_;
}

// |shell::Surface|
GrContext* GPUSurfaceGL::GetContext() {
  return context_.get();
}

// |shell::Surface|
flow::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() {
  return delegate_->GetExternalViewEmbedder();
}

}  // namespace shell
<|endoftext|>