Fix shared data access in raytracer, tidy up render loop

Render loop lost track of which blocks had been rendered due to a
synchronization issue. Specifically, std::vector<bool> is special-cased
as a bitfield and so is pretty much guaranteed not to be atomic unless
synchronized externally.

Also:

- Clean up types of a few variables
- Clean up openmp sharing type of variables (may result in better
  optimization)
- Replace shared rendered block count with an openmp reduction (results
  in fewer locks)

Fixes: lp:1608289
* https://bugs.launchpad.net/kicad/+bug/1608289
This commit is contained in:
Chris Pavlina 2016-08-28 02:05:49 -04:00
parent 67230ac8e7
commit 82ed0fde32
2 changed files with 22 additions and 14 deletions

View File

@ -353,33 +353,38 @@ void C3D_RENDER_RAYTRACING::rt_render_tracing( GLubyte *ptrPBO ,
{ {
m_isPreview = false; m_isPreview = false;
unsigned int nrBlocks = m_blockPositions.size(); // Cache the number of blocks const size_t nrBlocks = m_blockPositions.size();
unsigned startTime = GetRunningMicroSecs(); // Get time that started render this block const unsigned startTime = GetRunningMicroSecs();
bool breakLoop = false; // It will be used to break the loop bool breakLoop = false;
int numBlocksRendered = 0;
#pragma omp parallel for schedule(dynamic) #pragma omp parallel for schedule(dynamic) shared(breakLoop) \
for( signed int iBlock = 0; iBlock < (int)nrBlocks; iBlock++ ) firstprivate(ptrPBO, nrBlocks, startTime) reduction(+:numBlocksRendered) default(none)
for( size_t iBlock = 0; iBlock < nrBlocks; iBlock++ )
{ {
#pragma omp flush(breakLoop) #pragma omp flush(breakLoop)
if( !breakLoop ) // That is used to break the other threads if( !breakLoop )
{ {
// Check if this block was already processed bool process_block;
if( !m_blockPositionsWasProcessed[iBlock] )
// std::vector<bool> stuffs eight bools to each byte, so access to
// them can never be natively atomic.
#pragma omp critical(checkProcessBlock)
{ {
process_block = !m_blockPositionsWasProcessed[iBlock];
m_blockPositionsWasProcessed[iBlock] = true; m_blockPositionsWasProcessed[iBlock] = true;
}
// Render this block if( process_block )
{
rt_render_trace_block( ptrPBO, iBlock ); rt_render_trace_block( ptrPBO, iBlock );
numBlocksRendered++;
#pragma omp atomic
m_nrBlocksRenderProgress++;
// Check if it spend already some time render and request to exit // Check if it spend already some time render and request to exit
// to display the progress // to display the progress
#ifdef _OPENMP #ifdef _OPENMP
// This makes possible that only one thread (id 0) can check the time
if( omp_get_thread_num() == 0 ) if( omp_get_thread_num() == 0 )
#endif #endif
if( (GetRunningMicroSecs() - startTime) > 150000 ) if( (GetRunningMicroSecs() - startTime) > 150000 )
@ -391,6 +396,8 @@ void C3D_RENDER_RAYTRACING::rt_render_tracing( GLubyte *ptrPBO ,
} }
} }
m_nrBlocksRenderProgress += numBlocksRendered;
if( aStatusTextReporter ) if( aStatusTextReporter )
aStatusTextReporter->Report( wxString::Format( _( "Rendering: %.0f %%" ), aStatusTextReporter->Report( wxString::Format( _( "Rendering: %.0f %%" ),
(float)(m_nrBlocksRenderProgress * 100) / (float)(m_nrBlocksRenderProgress * 100) /

View File

@ -40,6 +40,7 @@
#include <plugins/3dapi/c3dmodel.h> #include <plugins/3dapi/c3dmodel.h>
#include <map> #include <map>
#include <cstddef>
/// Vector of materials /// Vector of materials
typedef std::vector< CBLINN_PHONG_MATERIAL > MODEL_MATERIALS; typedef std::vector< CBLINN_PHONG_MATERIAL > MODEL_MATERIALS;
@ -110,7 +111,7 @@ private:
unsigned long int m_stats_start_rendering_time; unsigned long int m_stats_start_rendering_time;
/// Save the number of blocks progress of the render /// Save the number of blocks progress of the render
unsigned int m_nrBlocksRenderProgress; size_t m_nrBlocksRenderProgress;
CPOSTSHADER_SSAO m_postshader_ssao; CPOSTSHADER_SSAO m_postshader_ssao;