Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: SFML-based slide show  (Read 3767 times)

0 Members and 1 Guest are viewing this topic.

stubler

  • Newbie
  • *
  • Posts: 17
    • View Profile
SFML-based slide show
« on: March 15, 2010, 07:32:42 pm »
I am working on a "slide show" project. The goal is to display multiple images simultaneously in different parts of the screen and to provide some sophisticated transitions as images enter and leave. It appears that SFML may have most of the features I need. I have hit several rough patches of road along the way and have been able to work through them with help from the SFML community (Many thanks!).

My current confusion centers around loading images via a background thread. I have written an image loader that employs a background worker thread in order to avoid pauses in the animation of the slide show. I have included the loader and some simple test code below. I encounter several problems with this code:
1) I still experience pauses in the execution
2) Some of the images are only rendered as white rectangles.
3) I am apparently leaking large amounts of memory (even though I delete the images).

Any insight to my problems would be greatly appreciated.

Note: I am using SFML 2.0 build 1444 because I need to use RenderImage until clipping paths are available.

Another Note: Upon re-examination, my "real" slide show routine does not appear to leak as much memory. the only real differences appear to be that the sprite that uses the image goes out of context and is destructed, and the RenderImage pointer that uses the sprite which uses the image is deleted immediately after the image is deleted. (Clear as mud?)

This is my test routine. The image file spinner.png is just a small image that I rotate to visualize hiccups in execution. The other images are from digital still cameras.
Code: [Select]

//-----------------------------------------------------------------------------
void LoaderTest()
{
  // Create a clock for measuring the time elapsed
  sf::Clock clock;

  // Create main window
  sf::RenderWindow canvas(sf::VideoMode(800, 600), "loaderTest");

  sf::Image spin_image;
  spin_image.LoadFromFile("C:/images/spinner.png");
  sf::Sprite spin_sprite(spin_image);
  spin_sprite.SetOrigin(spin_image.GetWidth()/2.0F, spin_image.GetHeight()/2.0F);
  spin_sprite.SetPosition(canvas.GetWidth()/2.0F, canvas.GetHeight()/2.0F);

  vector<string> fnames;
  fnames.push_back("C:/images/smallA.jpg");
  fnames.push_back("C:/images/largeA.jpg");
  fnames.push_back("C:/images/smallB.jpg");
  fnames.push_back("C:/images/largeB.jpg");

  SlideShowImageLoader loader;
  int idx = 0;
  sf::Image *pImage = 0;
  sf::Sprite image_sprite;
  std::string filename;

  // Start game loop
  float switchDelay = 2.0F;
  float switchTime = clock.GetElapsedTime();
  float currentTime = clock.GetElapsedTime();
  while (canvas.IsOpened())
  {
    // Process events
    sf::Event Event;
    while (canvas.GetEvent(Event))
    {
      // Close window : exit
      if (Event.Type == sf::Event::Closed)
        canvas.Close();

      // Escape key : exit
      else if (Event.Type == sf::Event::KeyPressed)
      {
        if (Event.Key.Code == sf::Key::Escape)
          canvas.Close();
      }
    }

    // queue up images
    while (loader.imagesTotal() < fnames.size())
      loader.RequestImageFile(fnames[idx++ % (int)fnames.size()]);

    // switch images periodically
    currentTime = clock.GetElapsedTime();
    if (currentTime >= switchTime)
    {
      if (pImage)
      {
        delete pImage;
        pImage = 0;
      }
      if (loader.DequeImage(filename, pImage))
      {
        image_sprite.SetImage(*pImage, true);
        image_sprite.SetOrigin(pImage->GetWidth()/2.0F, pImage->GetHeight()/2.0F);
        image_sprite.SetPosition(canvas.GetWidth()/2.0F, canvas.GetHeight()/2.0F);
        float scale = std::min((float)canvas.GetWidth()/pImage->GetWidth(), (float)canvas.GetHeight()/pImage->GetHeight());
        image_sprite.SetScale(scale, scale);
        switchTime = currentTime + switchDelay;
      }
    }

    // Display
    canvas.Clear();
    canvas.Draw(image_sprite);
    spin_sprite.SetRotation(currentTime * 180);
    canvas.Draw(spin_sprite);

    // Finally, display the rendered frame on screen
    canvas.Display();
  }
}


The SlideShowImageLoader.h looks like this:

Code: [Select]

#pragma once

#include <SFML/Graphics.hpp>
#include <deque>


typedef std::pair<std::string, sf::Image *> NamedImagePtr;

//-----------------------------------------------------------------------------
class SlideShowImageLoader
{
public:
  SlideShowImageLoader();
  virtual ~SlideShowImageLoader();

  static void LoaderLoop(void *pData);

  void RequestImageFile(std::string &filename);
  bool DequeImage(std::string &filename, sf::Image *&pImage);

  inline unsigned imagesAvailable() const { return (unsigned)responses.size(); }
  inline unsigned imagesPending() const { return (unsigned)requests.size(); }
  inline unsigned imagesTotal() const { return (unsigned)responses.size() + (unsigned)requests.size(); }


protected:
  bool                                exitSignal;
  sf::Thread                          loaderThread;
  sf::Mutex                           responseMutex;
  sf::Mutex                           requestMutex;
  std::deque<NamedImagePtr>           responses;
  std::deque<std::string>             requests;
};


SlideShowImageLoader.cpp
Code: [Select]

#include "SlideShowImageLoader.h"

//-----------------------------------------------------------------------------
SlideShowImageLoader::SlideShowImageLoader() : exitSignal(false), loaderThread(LoaderLoop, this)
{
  loaderThread.Launch();
}

//-----------------------------------------------------------------------------
SlideShowImageLoader::~SlideShowImageLoader()
{
  exitSignal = true;
  loaderThread.Wait();
  while (responses.size())
  {
    delete responses.front().second;
    responses.pop_front();
  }
}

//-----------------------------------------------------------------------------
void SlideShowImageLoader::LoaderLoop(void *pData)
{
  sf::Context context; // this line is needed for images to be loaded properly
  SlideShowImageLoader &loader = *(SlideShowImageLoader *)pData;
  while(!loader.exitSignal)
  {
    if (loader.requests.size())
    {
      loader.requestMutex.Lock();
      std::string filename = loader.requests.front();
      loader.requests.pop_front();
      loader.requestMutex.Unlock();

      sf::Image *pImage = new sf::Image();
      if (pImage->LoadFromFile(filename))
      {
        loader.responseMutex.Lock();
        loader.responses.push_back(NamedImagePtr(filename, pImage));
        loader.responseMutex.Unlock();
      }
    }
    sf::Sleep(0.01f);
  }
}

//-----------------------------------------------------------------------------
void SlideShowImageLoader::RequestImageFile(std::string &filename)
{
  requestMutex.Lock();
  requests.push_back(filename);
  requestMutex.Unlock();
}

//-----------------------------------------------------------------------------
bool SlideShowImageLoader::DequeImage(std::string &filename, sf::Image *&pImage)
{
  bool rval = false;
  if (responses.size())
  {
    responseMutex.Lock();
    filename = responses.front().first;
    pImage = responses.front().second;
    responses.pop_front();
    responseMutex.Unlock();
    rval = true;
  }
  return rval;
}
:lol:

stubler

  • Newbie
  • *
  • Posts: 17
    • View Profile
More Info
« Reply #1 on: March 15, 2010, 09:11:34 pm »
I just did a few more tests, in an attempt to isolate my problems.

In my first test, all I displayed was my "spinner" and only read and immediately deleted new images. My window updated smoothly.

In my second test, I preloaded my background loader and modified it to recycle images by pushing them back onto the end of the deque. Then, I commented out the part of my code that deleted images. Thus, my slide show would loop through several images without disk activity. During this test, I noticed mild hiccups as images appeared. Each of these appearances coincides with the creation of a RenderImage (and thus a drawing context).

It appears that the significant hiccups occur with the near simultaneous creation of a RenderImage and reading of a large image from disk.