fractorium/Source/Fractorium/GLEmberController.cpp
mfeemster 018ba26b5f --User changes
-Add support for multiple GPU devices.
  --These options are present in the command line and in Fractorium.
 -Change scheme of specifying devices from platform,device to just total device index.
  --Single number on the command line.
  --Change from combo boxes for device selection to a table of all devices in Fractorium.
 -Temporal samples defaults to 100 instead of 1000 which was needless overkill.

--Bug fixes
 -EmberAnimate, EmberRender, FractoriumSettings, FinalRenderDialog: Fix wrong order of arguments to Clamp() when assigning thread priority.
 -VariationsDC.h: Fix NVidia OpenCL compilation error in DCTriangleVariation.
 -FractoriumXformsColor.cpp: Checking for null pixmap pointer is not enough, must also check if the underlying buffer is null via call to QPixmap::isNull().

--Code changes
 -Ember.h: Add case for FLAME_MOTION_NONE and default in ApplyFlameMotion().
 -EmberMotion.h: Call base constructor.
 -EmberPch.h: #pragma once only on Windows.
 -EmberToXml.h:
  --Handle different types of exceptions.
  --Add default cases to ToString().
 -Isaac.h: Remove unused variable in constructor.
 -Point.h: Call base constructor in Color().
 -Renderer.h/cpp:
  --Add bool to Alloc() to only allocate memory for the histogram. Needed for multi-GPU.
  --Make CoordMap() return a const ref, not a pointer.
 -SheepTools.h:
  --Use 64-bit types like the rest of the code already does.
  --Fix some comment misspellings.
 -Timing.h: Make BeginTime(), EndTime(), ElapsedTime() and Format() be const functions.
 -Utils.h:
  --Add new functions Equal() and Split().
  --Handle more exception types in ReadFile().
  --Get rid of most legacy blending of C and C++ argument parsing.
 -XmlToEmber.h:
  --Get rid of most legacy blending of C and C++ code from flam3.
  --Remove some unused variables.
 -EmberAnimate:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
  --If a render fails, exit since there is no point in continuing an animation with a missing frame.
  --Pass variables to threaded save better, which most likely fixes a very subtle bug that existed before.
  --Remove some unused variables.
 -EmberGenome, EmberRender:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
 -EmberRender:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
  --Only print values when not rendering with OpenCL, since they're always 0 in that case.
 -EmberCLPch.h:
  --#pragma once only on Windows.
  --#include <atomic>.
 -IterOpenCLKernelCreator.h: Add new kernel for summing two histograms. This is needed for multi-GPU.
 -OpenCLWrapper.h:
  --Move all OpenCL info related code into its own class OpenCLInfo.
  --Add members to cache the values of global memory size and max allocation size.
 -RendererCL.h/cpp:
  --Redesign to accomodate multi-GPU.
  --Constructor now takes a vector of devices.
  --Remove DumpErrorReport() function, it's handled in the base.
  --ClearBuffer(), ReadPoints(), WritePoints(), ReadHist() and WriteHist() now optionally take a device index as a parameter.
  --MakeDmap() override and m_DmapCL member removed because it no longer applies since the histogram is always float since the last commit.
  --Add new function SumDeviceHist() to sum histograms from two devices by first copying to a temporary on the host, then a temporary on the device, then summing.
  --m_Calls member removed, as it's now per-device.
  --OpenCLWrapper removed.
  --m_Seeds member is now a vector of vector of seeds, to accomodate a separate and different array of seeds for each device.
  --Added member m_Devices, a vector of unique_ptr of RendererCLDevice.
 -EmberCommon.h
  --Added Devices() function to convert from a vector of device indices to a vector of platform,device indices.
  --Changed CreateRenderer() to accept a vector of devices to create a single RendererCL which will split work across multiple devices.
  --Added CreateRenderers() function to accept a vector of devices to create multiple RendererCL, each which will render on a single device.
  --Add more comments to some existing functions.
 -EmberCommonPch.h: #pragma once only on Windows.
 -EmberOptions.h:
  --Remove --platform option, it's just sequential device number now with the --device option.
  --Make --out be OPT_USE_RENDER instead of OPT_RENDER_ANIM since it's an error condition when animating. It makes no sense to write all frames to a single image.
  --Add Devices() function to parse comma separated --device option string and return a vector of device indices.
  --Make int and uint types be 64-bit, so intmax_t and size_t.
  --Make better use of macros.
 -JpegUtils.h: Make string parameters to WriteJpeg() and WritePng() be const ref.
 -All project files: Turn off buffer security check option in Visual Studio (/Gs-)
 -deployment.pri: Remove the line OTHER_FILES +=, it's pointless and was causing problems.
 -Ember.pro, EmberCL.pro: Add CONFIG += plugin, otherwise it wouldn't link.
 -EmberCL.pro: Add new files for multi-GPU support.
 -build_all.sh: use -j4 and QMAKE=${QMAKE:/usr/bin/qmake}
 -shared_settings.pri:
  -Add version string.
  -Remove old DESTDIR definitions.
  -Add the following lines or else nothing would build:
   CONFIG(release, debug|release) {
    CONFIG += warn_off
    DESTDIR = ../../../Bin/release
   }

   CONFIG(debug, debug|release) {
    DESTDIR = ../../../Bin/debug
   }

   QMAKE_POST_LINK += $$quote(cp --update ../../../Data/flam3-palettes.xml $${DESTDIR}$$escape_expand(\n\t))
   LIBS += -L/usr/lib -lpthread
 -AboutDialog.ui: Another futile attempt to make it look correct on Linux.
 -FinalRenderDialog.h/cpp:
  --Add support for multi-GPU.
  --Change from combo boxes for device selection to a table of all devices.
  --Ensure device selection makes sense.
  --Remove "FinalRender" prefix of various function names, it's implied given the context.
 -FinalRenderEmberController.h/cpp:
  --Add support for multi-GPU.
  --Change m_FinishedImageCount to be atomic.
  --Move CancelRender() from the base to FinalRenderEmberController<T>.
  --Refactor RenderComplete() to omit any progress related functionality or image saving since it can be potentially ran in a thread.
  --Consolidate setting various renderer fields into SyncGuiToRenderer().
 -Fractorium.cpp: Allow for resizing of the options dialog to show the entire device table.
 -FractoriumCommon.h: Add various functions to handle a table showing the available OpenCL devices on the system.
 -FractoriumEmberController.h/cpp: Remove m_FinalImageIndex, it's no longer needed.
 -FractoriumRender.cpp: Scale the interactive sub batch count and quality by the number of devices used.
 -FractoriumSettings.h/cpp:
  --Temporal samples defaults to 100 instead of 1000 which was needless overkill.
  --Add multi-GPU support, remove old device,platform pair.
 -FractoriumToolbar.cpp: Disable OpenCL toolbar button if there are no devices present on the system.
 -FractoriumOptionsDialog.h/cpp:
  --Add support for multi-GPU.
  --Consolidate more assignments in DataToGui().
  --Enable/disable CPU/OpenCL items in response to OpenCL checkbox event.
 -Misc: Convert almost everything to size_t for unsigned, intmax_t for signed.
2015-09-12 18:33:45 -07:00

272 lines
8.4 KiB
C++

#include "FractoriumPch.h"
#include "GLEmberController.h"
#include "FractoriumEmberController.h"
#include "Fractorium.h"
#include "GLWidget.h"
/// <summary>
/// Constructor which assigns pointers to the main window and the GLWidget.
/// </summary>
/// <param name="fractorium">Pointer to the main window</param>
/// <param name="glWidget">Pointer to the GLWidget</param>
GLEmberControllerBase::GLEmberControllerBase(Fractorium* fractorium, GLWidget* glWidget)
{
m_Fractorium = fractorium;
m_GL = glWidget;
m_AffineType = AffinePre;
m_HoverType = HoverNone;
m_DragState = DragNone;
m_DragModifier = 0;
}
/// <summary>
/// Empty destructor which does nothing.
/// </summary>
GLEmberControllerBase::~GLEmberControllerBase() { }
/// <summary>
/// Constructor which passes the pointers to the main window the GLWidget to the base,
/// then assigns the pointer to the parent controller.
/// </summary>
/// <param name="fractorium">Pointer to the main window</param>
/// <param name="glWidget">Pointer to the GLWidget</param>
/// <param name="controller">Pointer to the parent controller of the same template type</param>
template <typename T>
GLEmberController<T>::GLEmberController(Fractorium* fractorium, GLWidget* glWidget, FractoriumEmberController<T>* controller)
: GLEmberControllerBase(fractorium, glWidget)
{
GridStep = T(1.0 / 8.0);
m_FractoriumEmberController = controller;
m_HoverXform = nullptr;
m_SelectedXform = nullptr;
m_CenterDownX = 0;
m_CenterDownY = 0;
}
/// <summary>
/// Empty destructor which does nothing.
/// </summary>
template <typename T>
GLEmberController<T>::~GLEmberController() { }
/// <summary>
/// Check that the final output size of the current ember matches the dimensions passed in.
/// </summary>
/// <param name="w">The width to compare to</param>
/// <param name="h">The height to compare to</param>
/// <returns>True if any don't match, else false if they are both equal.</returns>
template <typename T>
bool GLEmberController<T>::CheckForSizeMismatch(int w, int h)
{
return (m_FractoriumEmberController->FinalRasW() != w || m_FractoriumEmberController->FinalRasH() != h);
}
/// <summary>
/// Reset the drag and hover state. Called in response setting a new ember as the current one.
/// </summary>
template <typename T>
void GLEmberController<T>::ResetMouseState()
{
m_HoverType = HoverNone;
m_HoverXform = nullptr;
m_SelectedXform = nullptr;
}
/// <summary>
/// Calculate the scale.
/// Used when dragging the right mouse button.
/// </summary>
/// <returns>The distance dragged in pixels</returns>
template <typename T>
T GLEmberController<T>::CalcScale()
{
//Can't operate using world coords here because every time scale changes, the world bounds change.
//So must instead calculate distance traveled based on window coords, which do not change outside of resize events.
v2T windowCenter(T(m_GL->width()) / T(2), T(m_GL->height()) / T(2));
v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y);
v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y);
T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter);
T lengthMouseDownFromCenterInPixels = glm::length(windowMouseDownDistanceFromCenter);
return lengthMousePosFromCenterInPixels - lengthMouseDownFromCenterInPixels;
}
/// <summary>
/// Calculate the rotation.
/// Used when dragging the right mouse button.
/// </summary>
/// <returns>The angular distance rotated from -180-180</returns>
template <typename T>
T GLEmberController<T>::CalcRotation()
{
T rotStart = NormalizeDeg180<T>(T(90) - (atan2(-m_MouseDownWorldPos.y, m_MouseDownWorldPos.x) * RAD_2_DEG_T));
T rot = NormalizeDeg180<T>(T(90) - (atan2(-m_MouseWorldPos.y, m_MouseWorldPos.x) * RAD_2_DEG_T));
return rotStart - rot;
}
/// <summary>
/// Snap the passed in world cartesian coordinate to the grid for rotation, scale or translation.
/// </summary>
/// <param name="vec">The world cartesian coordinate to be snapped</param>
/// <returns>The snapped world cartesian coordinate</returns>
template <typename T>
typename v3T GLEmberController<T>::SnapToGrid(v3T& vec)
{
v3T ret;
ret.x = glm::round(vec.x / GridStep) * GridStep;
ret.y = glm::round(vec.y / GridStep) * GridStep;
return ret;
}
/// <summary>
/// Snap the passed in world cartesian coordinate to the grid for rotation only.
/// </summary>
/// <param name="vec">The world cartesian coordinate to be snapped</param>
/// <param name="divisions">The divisions of a circle to use for snapping</param>
/// <returns>The snapped world cartesian coordinate</returns>
template <typename T>
typename v3T GLEmberController<T>::SnapToNormalizedAngle(v3T& vec, uint divisions)
{
T rsq, theta;
T bestRsq = numeric_limits<T>::max();
v3T c, best;
best.x = 1;
best.y = 0;
for (uint i = 0; i < divisions; i++)
{
theta = 2.0 * M_PI * T(i) / T(divisions);
c.x = cos(theta);
c.y = sin(theta);
rsq = glm::distance(vec, c);
if (rsq < bestRsq)
{
best = c;
bestRsq = rsq;
}
}
return best;
}
/// <summary>
/// Convert raster window coordinates to world cartesian coordinates.
/// </summary>
/// <param name="v">The window coordinates to convert</param>
/// <param name="flip">True to flip vertically, else don't.</param>
/// <returns>The converted world cartesian coordinates</returns>
template <typename T>
typename v3T GLEmberController<T>::WindowToWorld(v3T& v, bool flip)
{
v3T mouse(v.x, flip ? m_Viewport[3] - v.y : v.y, 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
v3T newCoords = glm::unProject(mouse, m_Modelview, m_Projection, m_Viewport);//Perform the calculation.
newCoords.z = 0;//For some reason, unProject() always comes back with the z coordinate as something other than 0. It should be 0 at all times.
return newCoords;
}
/// <summary>
/// Template specialization for querying the viewport, modelview and projection
/// matrices as floats.
/// </summary>
template <>
void GLEmberController<float>::QueryVMP()
{
m_GL->glGetIntegerv(GL_VIEWPORT, glm::value_ptr(m_Viewport));
m_GL->glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(m_Modelview));
m_GL->glGetFloatv(GL_PROJECTION_MATRIX, glm::value_ptr(m_Projection));
}
#ifdef DO_DOUBLE
/// <summary>
/// Template specialization for querying the viewport, modelview and projection
/// matrices as doubles.
/// </summary>
template <>
void GLEmberController<double>::QueryVMP()
{
m_GL->glGetIntegerv(GL_VIEWPORT, glm::value_ptr(m_Viewport));
m_GL->glGetDoublev(GL_MODELVIEW_MATRIX, glm::value_ptr(m_Modelview));
m_GL->glGetDoublev(GL_PROJECTION_MATRIX, glm::value_ptr(m_Projection));
}
#endif
/// <summary>
/// Template specialization for multiplying the current matrix
/// by an m4<float>.
/// </summary>
template <>
void GLEmberController<float>::MultMatrix(tmat4x4<float, glm::defaultp>& mat)
{
m_GL->glMultMatrixf(glm::value_ptr(mat));
}
#ifdef DO_DOUBLE
/// <summary>
/// Template specialization for multiplying the current matrix
/// by an m4<double>.
/// </summary>
template <>
void GLEmberController<double>::MultMatrix(tmat4x4<double, glm::defaultp>& mat)
{
m_GL->glMultMatrixd(glm::value_ptr(mat));
}
#endif
/// <summary>
/// Query the matrices currently being used.
/// Debugging function, unused.
/// </summary>
/// <param name="print">True to print values, else false.</param>
template <typename T>
void GLEmberController<T>::QueryMatrices(bool print)
{
RendererBase* renderer = m_FractoriumEmberController->Renderer();
if (renderer)
{
double unitX = fabs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0;
double unitY = fabs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0;
m_GL->glMatrixMode(GL_PROJECTION);
m_GL->glPushMatrix();
m_GL->glLoadIdentity();
m_GL->glOrtho(-unitX, unitX, -unitY, unitY, -1, 1);
m_GL->glMatrixMode(GL_MODELVIEW);
m_GL->glPushMatrix();
m_GL->glLoadIdentity();
QueryVMP();
m_GL->glMatrixMode(GL_PROJECTION);
m_GL->glPopMatrix();
m_GL->glMatrixMode(GL_MODELVIEW);
m_GL->glPopMatrix();
if (print)
{
for (size_t i = 0; i < 4; i++)
qDebug() << "Viewport[" << i << "] = " << m_Viewport[i] << endl;
for (size_t i = 0; i < 16; i++)
qDebug() << "Modelview[" << i << "] = " << glm::value_ptr(m_Modelview)[i] << endl;
for (size_t i = 0; i < 16; i++)
qDebug() << "Projection[" << i << "] = " << glm::value_ptr(m_Projection)[i] << endl;
}
}
}
template class GLEmberController<float>;
#ifdef DO_DOUBLE
template class GLEmberController<double>;
#endif