Skip to main content
gamedev.net
🔒 Locked

Using a for loop for asynchronous loading the scene objects

Started by ProspectiveDirectX12User Jul 19, 2014 at 12:16 PM 4 replies 4.9k views
Original Post
ProspectiveDirectX12User
ProspectiveDirectX12User

Hi,

Is it possible to use a for loop to load some .cmo objects asynchronously?

The following code is from the Visual Studio 3D Starter Kit (Windows 8.1 Store App):


	// Load the scene objects.

	auto loadMeshTask = Mesh::LoadFromFileAsync(
		m_graphics,
		L"gamelevel.cmo",
		L"",
		L"",
		m_meshModels)
		.then([this]()
	{
		return Mesh::LoadFromFileAsync(
			m_graphics,
			L"teapot.cmo",
			L"",
			L"",
			m_meshModels,
			false  // Do not clear the vector of meshes
			);
	})
		.then([this]()
	{
		// Initialize animated models.
		// ...
	});



		// Loads a scene from the specified file, returning a vector of mesh objects.
		static concurrency::task<void> LoadFromFileAsync(
			Graphics& graphics,
			const std::wstring& meshFilename,
			const std::wstring& shaderPathLocation,
			const std::wstring& texturePathLocation,
			std::vector<Mesh*>& loadedMeshes,
			bool clearLoadedMeshesVector = true
			)
		{
			auto folder = Windows::ApplicationModel::Package::Current->InstalledLocation;

			// Start by loading the file.
			return concurrency::create_task(folder->GetFileAsync(Platform::StringReference(meshFilename.c_str())))
				// Then open the file.
				.then([](Windows::Storage::StorageFile^ file)
			{
				return Windows::Storage::FileIO::ReadBufferAsync(file);
			})
				// Then read the meshes.
				.then([&graphics, shaderPathLocation, texturePathLocation, &loadedMeshes](Windows::Storage::Streams::IBuffer^ buffer)
			{
				auto reader = Windows::Storage::Streams::DataReader::FromBuffer(buffer);
				reader->ByteOrder = Windows::Storage::Streams::ByteOrder::LittleEndian;
				reader->UnicodeEncoding = Windows::Storage::Streams::UnicodeEncoding::Utf8;

				// Read how many meshes are part of the scene.
				std::shared_ptr<UINT> remainingMeshesToRead = std::make_shared<UINT>(reader->ReadUInt32());

				if (*remainingMeshesToRead == 0)
				{
					return concurrency::create_task([](){});
				}

				// For each mesh in the scene, load it from the file.

				return concurrency::extras::create_iterative_task([reader, &graphics, shaderPathLocation, texturePathLocation, &loadedMeshes, remainingMeshesToRead]()
				{
					return Mesh::ReadAsync(reader, graphics, shaderPathLocation, texturePathLocation)
						.then([&loadedMeshes, remainingMeshesToRead](Mesh* mesh) -> bool
					{
						if (mesh != nullptr)
						{
							loadedMeshes.push_back(mesh);
						}

						// Return true to continue iterating, false to stop.

						*remainingMeshesToRead = *remainingMeshesToRead - 1;

						return *remainingMeshesToRead > 0;
					});
				});
			});
		}

This code loads the gamelevel.cmo and the teapot.cmo files. But i have about 20 .cmo files to load.

Thanks,

Regards,

Daniel Klein

ankhd
ankhd

try it.

//auto looks like lazy foo what type is it

auto loadMeshTask you would need to keep track of the data so you know when the file was loaded

how do you know when the data is loaded. is there a call back function. Maybe that is what this is loadMeshTask

http://msdn.microsoft.com/en-us/library/hh191443.aspx

ProspectiveDirectX12User
ProspectiveDirectX12User

Thanks for the hints:

I tried it, but it's not that easy (for me).

I changed the code to avoid laziness:


old:	auto loadMeshTask			= Mesh::LoadFromFileAsync(
new:	concurrency::task <void> loadMeshTask	= Mesh::LoadFromFileAsync(

It's defined in ppltasks.h:


template<>
class task<void>
{
public:
    /// <summary>
    ///     The type of the result an object of this class produces.
    /// </summary>
    /**/
    typedef void result_type;
...

So the result type is:

Mesh* mesh

If i understand this correctly:

As soon as the first mesh ist loaded (gamelevel.cmo) .then([this]() is executed to load the second mesh (teapot.cmo)

Your link to "Asynchronous Programming with Async and Await (C# and Visual Basic)" helped me (a bit) to understand asynchrony FileIO-Streams::DataReader
(but it's not for C++).

I need some code like this (compiling failed):


concurrency::task <void> loadMeshTask = Mesh::LoadFromFileAsync(m_graphics, L"gamelevel.cmo", L"", L"", m_meshModels)

 for (UINT current_mesh_number = 0; current_mesh_number < 20; current_mesh_number++)
 {
  .then([this]()
  {
   // Load the current mesh from a separate file and add it to the vector of meshes:
   return Mesh::LoadFromFileAsync(m_graphics, L"teapot.cmo", L"", L"", m_meshModels, false);
  })
 }

After a few tests i think it's not possible.

Regards,
Daniel Klein

ankhd
ankhd

I don't think that function is doing what you think it is.

It I think allows for the file to be open while someone else has it open. and not loading in the background.

what you may need to do is or the way I not long a go did was all the functions I had in my initialize method I made a template class that all data that needs to be loaded

ist placed in a big array or vector. Then I created a back ground thread that would use this list of data and types to load all files each time it loads it pops that from the stack when the stack is empty kill the thread tell the main ap that the data is loaded.

the thing is that it dosen't help much in the fact you need the data befor the app can start.

To get over this fact I had 2 paths of loading one lot for the startup so the user can use menu. But in my app to start a game it only takes about 2 seconds on the menu and then

the user is still waiting for the back ground thread to load all its data as it takes 2 minutes to load.

I allow the games start load screen to pop up in that case and it looks like its loading data for the game to begin but its still loading back ground data then the game would start

when it completed.

...

Second thougths try something like this. This Is complex look at replie 7 he's got what you want. this is beyond me now. Link

So this is what all this talk about Lambda is bizarre bizarre.

ProspectiveDirectX12User
ProspectiveDirectX12User

Thanks again for your explanation and the link.

From your Link i got these very helpful links (Perils Of Lambda Capture) and (Keeping apps fast and fluid with asynchrony in the Windows Runtime):

http://blogs.msdn.com/b/nativeconcurrency/archive/2012/01/29/perils-of-lambda-capture.aspx
http://blogs.msdn.com/b/windowsappdev/archive/2012/03/20/keeping-apps-fast-and-fluid-with-asynchrony-in-the-windows-runtime.aspx

And (Asynchronous Programming in C++ Using PPL):
http://msdn.microsoft.com/en-us/magazine/hh781020.aspx

First thing i learned: Never let the address of a local variable get into a task’s lambda.
The right way to do it is to allocate it on the heap and capture the pointer to it by value.
So i started using a heap pointer (smart pointer to avoid problems with deleting the pointer) to use the pointer in more than one continuation like this:


std::shared_ptr<std::pair<UINT, std::wstring>> p = std::make_shared<std::pair<UINT, std::wstring>>(0, L"");

 concurrency::task<void> t([p]()
 {
  p->first = 10;
  p->second = L"Test1";
 });

 t.then([p]()
 {
  UINT test = p->first;
  std::wstring f = p->second;
 });

This tutorial seems to be a solution to my problem (How to put a PPLTasks continuation chain into a loop):

http://blogs.msdn.com/b/nativeconcurrency/archive/2012/05/09/how-to-put-a-ppltasks-continuation-chain-into-a-loop.aspx

create_iterative_task gives you following advantages for free, when compared with a simple hand-crafted loop:

1.It makes your asynchronous logic neat and easy to understand;
2.You CAN attach continuations to it, and it does not suffer from the out of memory issue;
3.It can be canceled with a cancellation token;
4.It will correctly propagate exceptions in the loop to its continuations

The create_iterative_task method helps you build an asynchronous loop.
Suppose you want to read and process a file line by line without blocking,
and close the file after you’re done reading it. All you need is a file reader object that yields one line at a time,
and a call to the create_iterative_task method, that accepts a lambda that will be invoked for each line of the file.

Example:


cancellation_token ct;

create_iterative_task([]
{
    return create_task(networkStream.readAsync())

    .then(writeFileAsync)

    .then([]
    {
        return is_finished();

    }); // a boolean task

}, ct)

 .then(task<void> t)
 {
 // continuations gets executed after loop

   try
   {
       t.get();
   }
  catch (Exception)
  {

  }

   // do clean-ups
}

Now i have to impement this. I hope it works as supposed.

Regards,
Daniel Klein

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.