2014-07-08 03:11:14 -04:00
# include "FractoriumPch.h"
# include "Fractorium.h"
/// <summary>
/// Initialize the library tree UI.
/// </summary>
void Fractorium : : InitLibraryUI ( )
{
connect ( ui . LibraryTree , SIGNAL ( itemChanged ( QTreeWidgetItem * , int ) ) , this , SLOT ( OnEmberTreeItemChanged ( QTreeWidgetItem * , int ) ) , Qt : : QueuedConnection ) ;
connect ( ui . LibraryTree , SIGNAL ( itemDoubleClicked ( QTreeWidgetItem * , int ) ) , this , SLOT ( OnEmberTreeItemDoubleClicked ( QTreeWidgetItem * , int ) ) , Qt : : QueuedConnection ) ;
2015-03-21 18:27:37 -04:00
connect ( ui . LibraryTree , SIGNAL ( itemActivated ( QTreeWidgetItem * , int ) ) , this , SLOT ( OnEmberTreeItemDoubleClicked ( QTreeWidgetItem * , int ) ) , Qt : : QueuedConnection ) ;
}
/// <summary>
/// Get the index of the currently selected ember in the library tree.
/// </summary>
/// <returns>A pair containing the index of the item clicked and a pointer to the item</param>
pair < size_t , QTreeWidgetItem * > Fractorium : : GetCurrentEmberIndex ( )
{
size_t index = 0 ;
QTreeWidgetItem * item = nullptr ;
QTreeWidget * tree = ui . LibraryTree ;
if ( QTreeWidgetItem * top = tree - > topLevelItem ( 0 ) )
{
for ( int i = 0 ; i < top - > childCount ( ) ; i + + ) //Iterate through all of the children, which will represent the open embers.
{
item = top - > child ( index ) ;
if ( item & & ! item - > isSelected ( ) )
index + + ;
else
break ;
}
}
return pair < size_t , QTreeWidgetItem * > ( index , item ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Slot function to be called via QMetaObject::invokeMethod() to update preview images in the preview thread.
/// </summary>
/// <param name="item">The item double clicked on</param>
/// <param name="v">The vector holding the RGBA bitmap</param>
2014-12-11 00:50:15 -05:00
/// <param name="w">The width of the bitmap</param>
/// <param name="h">The height of the bitmap</param>
void Fractorium : : SetLibraryTreeItemData ( EmberTreeWidgetItemBase * item , vector < byte > & v , uint w , uint h )
2014-07-08 03:11:14 -04:00
{
2014-12-11 00:50:15 -05:00
item - > SetImage ( v , w , h ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Set all libary tree entries to the name of the corresponding ember they represent.
/// </summary>
template < typename T >
void FractoriumEmberController < T > : : SyncNames ( )
{
EmberTreeWidgetItem < T > * item ;
QTreeWidget * tree = m_Fractorium - > ui . LibraryTree ;
tree - > blockSignals ( true ) ;
2015-03-21 18:27:37 -04:00
if ( QTreeWidgetItem * top = tree - > topLevelItem ( 0 ) )
2014-07-08 03:11:14 -04:00
{
for ( int i = 0 ; i < top - > childCount ( ) ; i + + ) //Iterate through all of the children, which will represent the open embers.
{
2014-10-14 11:53:15 -04:00
if ( ( item = dynamic_cast < EmberTreeWidgetItem < T > * > ( top - > child ( i ) ) ) & & i < m_EmberFile . Size ( ) ) //Cast the child widget to the EmberTreeWidgetItem type.
2014-07-08 03:11:14 -04:00
item - > setText ( 0 , QString : : fromStdString ( m_EmberFile . m_Embers [ i ] . m_Name ) ) ;
}
}
tree - > blockSignals ( false ) ;
}
2015-03-21 18:27:37 -04:00
/// <summary>
/// Set all libary tree entries to point to the underlying ember they represent.
/// </summary>
template < typename T >
void FractoriumEmberController < T > : : SyncPointers ( )
{
EmberTreeWidgetItem < T > * item ;
QTreeWidget * tree = m_Fractorium - > ui . LibraryTree ;
tree - > blockSignals ( true ) ;
if ( QTreeWidgetItem * top = tree - > topLevelItem ( 0 ) )
{
size_t childCount = top - > childCount ( ) ;
for ( int i = 0 ; i < childCount ; i + + ) //Iterate through all of the children, which will represent the open embers.
{
if ( ( item = dynamic_cast < EmberTreeWidgetItem < T > * > ( top - > child ( i ) ) ) & & i < m_EmberFile . Size ( ) ) //Cast the child widget to the EmberTreeWidgetItem type.
item - > SetEmberPointer ( & m_EmberFile . m_Embers [ i ] ) ;
}
}
tree - > blockSignals ( false ) ;
}
2014-07-08 03:11:14 -04:00
/// <summary>
/// Fill the library tree with the names of the embers in the
/// currently opened file.
/// Start preview render thread.
/// </summary>
/// <param name="selectIndex">After the tree is filled, select this index. Pass -1 to omit selecting an index.</param>
template < typename T >
void FractoriumEmberController < T > : : FillLibraryTree ( int selectIndex )
{
2014-12-06 00:05:09 -05:00
uint i , j , size = 64 ;
2014-07-08 03:11:14 -04:00
QTreeWidget * tree = m_Fractorium - > ui . LibraryTree ;
2014-12-06 00:05:09 -05:00
vector < byte > v ( size * size * 4 ) ;
2014-07-08 03:11:14 -04:00
StopPreviewRender ( ) ;
tree - > clear ( ) ;
QCoreApplication : : flush ( ) ;
tree - > blockSignals ( true ) ;
QTreeWidgetItem * fileItem = new QTreeWidgetItem ( tree ) ;
QFileInfo info ( m_EmberFile . m_Filename ) ;
fileItem - > setText ( 0 , info . fileName ( ) ) ;
fileItem - > setToolTip ( 0 , m_EmberFile . m_Filename ) ;
fileItem - > setFlags ( Qt : : ItemIsEnabled | Qt : : ItemIsEditable | Qt : : ItemIsSelectable ) ;
2014-10-14 11:53:15 -04:00
for ( j = 0 ; j < m_EmberFile . Size ( ) ; j + + )
2014-07-08 03:11:14 -04:00
{
Ember < T > * ember = & m_EmberFile . m_Embers [ j ] ;
EmberTreeWidgetItem < T > * emberItem = new EmberTreeWidgetItem < T > ( ember , fileItem ) ;
emberItem - > setFlags ( Qt : : ItemIsEnabled | Qt : : ItemIsEditable | Qt : : ItemIsSelectable ) ;
if ( ember - > m_Name . empty ( ) )
2014-10-14 11:53:15 -04:00
emberItem - > setText ( 0 , ToString ( j ) ) ;
2014-07-08 03:11:14 -04:00
else
emberItem - > setText ( 0 , ember - > m_Name . c_str ( ) ) ;
emberItem - > setToolTip ( 0 , emberItem - > text ( 0 ) ) ;
emberItem - > SetImage ( v , size , size ) ;
}
tree - > blockSignals ( false ) ;
if ( selectIndex ! = - 1 )
if ( QTreeWidgetItem * top = tree - > topLevelItem ( 0 ) )
if ( EmberTreeWidgetItem < T > * emberItem = dynamic_cast < EmberTreeWidgetItem < T > * > ( top - > child ( selectIndex ) ) )
emberItem - > setSelected ( true ) ;
QCoreApplication : : flush ( ) ;
2014-10-14 11:53:15 -04:00
RenderPreviews ( 0 , m_EmberFile . Size ( ) ) ;
2014-07-08 03:11:14 -04:00
tree - > expandAll ( ) ;
}
/// <summary>
/// Update the library tree with the newly added embers (most likely from pasting) and
/// only render previews for the new ones, without clearing the entire tree.
/// </summary>
template < typename T >
void FractoriumEmberController < T > : : UpdateLibraryTree ( )
{
2014-12-06 00:05:09 -05:00
uint i , size = 64 ;
2014-07-08 03:11:14 -04:00
QTreeWidget * tree = m_Fractorium - > ui . LibraryTree ;
2014-12-06 00:05:09 -05:00
vector < byte > v ( size * size * 4 ) ;
2014-07-08 03:11:14 -04:00
if ( QTreeWidgetItem * top = tree - > topLevelItem ( 0 ) )
{
int childCount = top - > childCount ( ) ;
tree - > blockSignals ( true ) ;
2014-10-14 11:53:15 -04:00
for ( i = childCount ; i < m_EmberFile . Size ( ) ; i + + )
2014-07-08 03:11:14 -04:00
{
Ember < T > * ember = & m_EmberFile . m_Embers [ i ] ;
EmberTreeWidgetItem < T > * emberItem = new EmberTreeWidgetItem < T > ( ember , top ) ;
emberItem - > setFlags ( Qt : : ItemIsEnabled | Qt : : ItemIsEditable | Qt : : ItemIsSelectable ) ;
if ( ember - > m_Name . empty ( ) )
2014-10-14 11:53:15 -04:00
emberItem - > setText ( 0 , ToString ( i ) ) ;
2014-07-08 03:11:14 -04:00
else
emberItem - > setText ( 0 , ember - > m_Name . c_str ( ) ) ;
emberItem - > setToolTip ( 0 , emberItem - > text ( 0 ) ) ;
emberItem - > SetImage ( v , size , size ) ;
}
//When adding elements to the vector, they may have been reshuffled which will have invalidated
//the pointers contained in the EmberTreeWidgetItems. So reassign all pointers here.
2015-03-21 18:27:37 -04:00
SyncPointers ( ) ;
2014-07-08 03:11:14 -04:00
tree - > blockSignals ( false ) ;
2014-10-14 11:53:15 -04:00
RenderPreviews ( childCount , m_EmberFile . Size ( ) ) ;
2014-07-08 03:11:14 -04:00
}
}
/// <summary>
/// Copy the text of the item which was changed to the name of the current ember.
/// Ensure all names are unique in the opened file.
/// This seems to be called spuriously, so we do a check inside to make sure
/// the text was actually changed.
/// We also have to wrap the dynamic_cast call in a try/catch block because this can
/// be called on a widget that has already been deleted.
/// </summary>
/// <param name="item">The libary tree item changed</param>
/// <param name="col">The column clicked, ignored.</param>
template < typename T >
void FractoriumEmberController < T > : : EmberTreeItemChanged ( QTreeWidgetItem * item , int col )
{
try
{
QTreeWidget * tree = m_Fractorium - > ui . LibraryTree ;
EmberTreeWidgetItem < T > * emberItem = dynamic_cast < EmberTreeWidgetItem < T > * > ( item ) ;
if ( emberItem )
{
string oldName = emberItem - > GetEmber ( ) - > m_Name ; //First preserve the previous name.
tree - > blockSignals ( true ) ;
emberItem - > UpdateEmberName ( ) ; //Copy edit text to the ember's name variable.
m_EmberFile . MakeNamesUnique ( ) ; //Ensure all names remain unique.
SyncNames ( ) ; //Copy all ember names to the tree items since some might have changed to be made unique.
string newName = emberItem - > GetEmber ( ) - > m_Name ; //Get the new, final, unique name.
if ( m_Ember . m_Name = = oldName & & oldName ! = newName ) //If the ember edited was the current one, and the name was indeed changed, update the name of the current one.
{
m_Ember . m_Name = newName ;
m_LastSaveCurrent = " " ; //Reset will force the dialog to show on the next save current since the user probably wants a different name.
}
2015-07-15 23:27:32 -04:00
2014-07-08 03:11:14 -04:00
tree - > blockSignals ( false ) ;
}
2015-07-23 21:16:36 -04:00
else if ( auto parentItem = dynamic_cast < QTreeWidgetItem * > ( item ) )
2014-07-08 03:11:14 -04:00
{
QString text = parentItem - > text ( 0 ) ;
if ( text ! = " " )
{
m_EmberFile . m_Filename = text ;
m_LastSaveAll = " " ; //Reset will force the dialog to show on the next save all since the user probably wants a different name.
}
}
}
--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 21:33:45 -04:00
catch ( const std : : exception & e )
2014-07-08 03:11:14 -04:00
{
qDebug ( ) < < " FractoriumEmberController<T>::EmberTreeItemChanged() : Exception thrown: " < < e . what ( ) ;
}
}
void Fractorium : : OnEmberTreeItemChanged ( QTreeWidgetItem * item , int col ) { m_Controller - > EmberTreeItemChanged ( item , col ) ; }
/// <summary>
/// Set the current ember to the selected item.
/// Clears the undo state.
/// Resets the rendering process.
/// Called when the user double clicks on a library tree item.
2015-08-10 23:10:23 -04:00
/// This will get called twice for some reason, and there's no way to prevent it.
/// Doesn't seem to cause any problems.
2014-07-08 03:11:14 -04:00
/// </summary>
/// <param name="item">The item double clicked on</param>
/// <param name="col">The column clicked, ignored.</param>
template < typename T >
void FractoriumEmberController < T > : : EmberTreeItemDoubleClicked ( QTreeWidgetItem * item , int col )
{
if ( EmberTreeWidgetItem < T > * emberItem = dynamic_cast < EmberTreeWidgetItem < T > * > ( item ) )
{
ClearUndo ( ) ;
SetEmber ( * emberItem - > GetEmber ( ) ) ;
}
}
void Fractorium : : OnEmberTreeItemDoubleClicked ( QTreeWidgetItem * item , int col ) { m_Controller - > EmberTreeItemDoubleClicked ( item , col ) ; }
2015-03-21 18:27:37 -04:00
/// <summary>
/// Delete the currently selected item in the tree.
/// Note this is not necessarilly the current ember, it's just the item
/// in the tree that is selected.
/// </summary>
/// <param name="p">A pair containing the index of the item clicked and a pointer to the item</param>
template < typename T >
void FractoriumEmberController < T > : : Delete ( const pair < size_t , QTreeWidgetItem * > & p )
{
QTreeWidget * tree = m_Fractorium - > ui . LibraryTree ;
tree - > blockSignals ( true ) ;
if ( m_EmberFile . Delete ( p . first ) )
{
delete p . second ;
SyncPointers ( ) ;
}
tree - > blockSignals ( false ) ;
//If there is now only one item left and it wasn't selected, select it.
if ( QTreeWidgetItem * top = tree - > topLevelItem ( 0 ) )
{
if ( top - > childCount ( ) = = 1 )
if ( auto item = dynamic_cast < EmberTreeWidgetItem < T > * > ( top - > child ( 0 ) ) )
if ( item - > GetEmber ( ) - > m_Name ! = m_Ember . m_Name )
EmberTreeItemDoubleClicked ( top - > child ( 0 ) , 0 ) ;
}
}
/// <summary>
/// Called when the user presses and releases the delete key while the library tree has the focus,
/// and an item is selected.
/// </summary>
/// <param name="p">A pair containing the index of the item clicked and a pointer to the item</param>
void Fractorium : : OnDelete ( const pair < size_t , QTreeWidgetItem * > & p )
{
m_Controller - > Delete ( p ) ;
}
2014-07-08 03:11:14 -04:00
/// <summary>
/// Stop the preview renderer if it's already running.
/// Clear all of the existing preview images, then start the preview rendering thread.
/// Optionally only render previews for a subset of all open embers.
/// </summary>
/// <param name="start">The 0-based index to start rendering previews for</param>
/// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param>
template < typename T >
2014-12-06 00:05:09 -05:00
void FractoriumEmberController < T > : : RenderPreviews ( uint start , uint end )
2014-07-08 03:11:14 -04:00
{
StopPreviewRender ( ) ;
if ( start = = UINT_MAX & & end = = UINT_MAX )
{
QTreeWidget * tree = m_Fractorium - > ui . LibraryTree ;
tree - > blockSignals ( true ) ;
if ( QTreeWidgetItem * top = tree - > topLevelItem ( 0 ) )
{
int childCount = top - > childCount ( ) ;
2014-12-06 00:05:09 -05:00
vector < byte > emptyPreview ( PREVIEW_SIZE * PREVIEW_SIZE * 3 ) ;
2014-07-08 03:11:14 -04:00
for ( int i = 0 ; i < childCount ; i + + )
if ( EmberTreeWidgetItem < T > * treeItem = dynamic_cast < EmberTreeWidgetItem < T > * > ( top - > child ( i ) ) )
treeItem - > SetImage ( emptyPreview , PREVIEW_SIZE , PREVIEW_SIZE ) ;
}
tree - > blockSignals ( false ) ;
2014-10-14 11:53:15 -04:00
m_PreviewResult = QtConcurrent : : run ( m_PreviewRenderFunc , 0 , m_EmberFile . Size ( ) ) ;
2014-07-08 03:11:14 -04:00
}
else
m_PreviewResult = QtConcurrent : : run ( m_PreviewRenderFunc , start , end ) ;
}
/// <summary>
/// Stop the preview rendering thread.
/// </summary>
template < typename T >
void FractoriumEmberController < T > : : StopPreviewRender ( )
{
m_PreviewRun = false ;
while ( m_PreviewRunning )
QApplication : : processEvents ( ) ;
m_PreviewResult . cancel ( ) ;
while ( m_PreviewResult . isRunning ( ) )
QApplication : : processEvents ( ) ;
QCoreApplication : : sendPostedEvents ( m_Fractorium - > ui . LibraryTree ) ;
QCoreApplication : : flush ( ) ;
}
2014-12-11 00:50:15 -05:00
template class FractoriumEmberController < float > ;
# ifdef DO_DOUBLE
template class FractoriumEmberController < double > ;
# endif