Wednesday, September 7, 2016

State reflection

Overview

The Stingray engine has two controller threads -- the main thread and the render thread. These two threads build up work for our job system, which is distributed on the remaining threads. The main thread and the render thread are pipelined, so that while the main thread runs the simulation/update for frame N, the render thread is processing the rendering work for the previous frame (N-1). This post will dive into the details how state is propagated from the main thread to the render thread.

I will use code snippets to explain how the state reflection works. It's mostly actual code from the engine but it has been cleaned up to a certain extent. Some stuff has been renamed and/or removed to make it easier to understand what's going on.

The main loop

Here is a slimmed down version of the update loop which is part of the main thread:

while (!quit())
{
    // Calls out to the mandatory user supplied `update` Lua function, Lua is used 
    // as a scripting language to manipulate objects. From Lua worlds, objects etc
    // can be created, manipulated, destroyed, etc. All these changes are recorded
    // on a `StateStream` that is a part of each world.
    _game->update();

    // Flush state changes recorded on the `StateStream` for each world to
    // the rendering world representation.
    unsigned n_worlds = _worlds.size();
    for (uint32_t i = 0; i < n_worlds; ++i) {
        auto &world = *_worlds[i];
        _render_interface->update_world(world);
    }

    // Begin a new render frame.
    _render_interface->begin_frame();

    // Calls out to the user supplied `render` Lua function. It's up to the script
    // to call render on worlds(). The script controls what camera and viewport
    // are used when rendering the world.
    _game->render();

    // Present the frame.
    _render_interface->present_frame();

    // End frame.
    _render_interface->end_frame(_delta_time);

    // Never let the main thread run more than 1 frame a head of the render thread.
    _render_interface->wait_for_fence(_frame_fence);

    // Create a new fence for the next frame.
    _frame_fence = _render_interface->create_fence();
}

First thing to point out is the _render_interface. This is not a class full of virtual functions that some other class can inherit from and override as the name might suggest. The word "interface" is used in the sense that it's used to communicate from one thread to another. So in this context the _render_interface is used to post messages from the main thread to the render thread.

As said in the first comment in the code snippet above, Lua is used as our scripting language and from Lua things such as worlds, objects, etc can be created, destroyed, manipulated, etc.

The state between the main thread and the render thread is very rarely shared, instead each thread has its own representation and when state is changed on the main thread that state is reflected over to the render thread. E.g., the MeshObject, which is the representation of a mesh with vertex buffers, materials, textures, shaders, skinning, data etc to be rendered, is the main thread representation and RenderMeshObject is the corresponding render thread representation. All objects that have a representation on both the main and render thread are setup to work the same way:

class MeshObject : public RenderStateObject
{
};

class RenderMeshObject : public RenderObject
{
};

The corresponding render thread class is prefixed with Render. We use this naming convention for all objects that have both a main and a render thread representation.

The main thread objects inherit from RenderStateObject and the render thread objects inherit from RenderObject. These structs are defined as:

struct RenderStateObject
{
    uint32_t render_handle;
    StateReflection *state_reflection;
};

struct RenderObject
{
    uint32_t type;
};

The render_handle is an ID that identifies the corresponding object on the render thread. state_reflection is a stream of data that is used to propagate state changes from the main thread to the render thread. type is an enum used to identify the type of render objects.

Object creation

In Stingray a world is a container of renderable objects, physical objects, sounds, etc. On the main thread, it is represented by the World class, and on the render thread by a RenderWorld.

When a MeshObject is created in a world on the main thread, there's an explicit call to WorldRenderInterface::create() to create the corresponding render thread representation:

MeshObject *mesh_object = MAKE_NEW(_allocator, MeshObject);
_world_render_interface.create(mesh_object);

The purpose of the call to WorldRenderInterface::create is to explicitly create the render thread representation, acquire a render_handle and to post that to the render thread:

void WorldRenderInterface::create(MeshObject *mesh_object)
{
    // Get a unique render handle.
    mesh_object->render_handle = new_render_handle();

    // Set the state_reflection pointer, more about this later.
    mesh_object->state_reflection = &_state_reflection;

    // Create the render thread representation.
    RenderMeshObject *render_mesh_object = MAKE_NEW(_allocator, RenderMeshObject);

    // Pass the data to the render thread
    create_object(mesh_object->render_handle, RenderMeshObject::TYPE, render_mesh_object);
}

The new_render_handle function speaks for itself.

uint32_t WorldRenderInterface::new_render_handle()
{
    if (_free_render_handles.any()) {
        uint32_t handle = _free_render_handles.back();
        _free_render_handles.pop_back();
        return handle;
    } else
        return _render_handle++;
}

There is a recycling mechanism for the render handles and a similar pattern reoccurs at several places in the engine. The release_render_handle function together with the new_render_handle function should give the complete picture of how it works.

void WorlRenderInterface::release_render_handle(uint32_t handle)
{
    _free_render_handles.push_back(handle);
}

There is one WorldRenderInterface per world which contains the _state_reflection that is used by the world and all of its objects to communicate with the render thread. The StateReflection in its simplest form is defined as:

struct StateReflection
{
    StateStream *state_stream;
};

The create_object function needs a bit more explanation though:

void WorldRenderInterface::create_object(uint32_t render_handle, RenderObject::Type type, void *user_data)
{
    // Allocate a message on the `state_stream`.
    ObjectManagementPackage *omp;
    alloc_message(_state_reflection.state_stream, WorldRenderInterface::CREATE, &omp);

    omp->object_type = RenderWorld::TYPE;
    omp->render_handle = render_handle;
    omp->type = type;
    omp->user_data = user_data;
}

What happens here is that alloc_message will allocate enough bytes to make room for a MessageHeader together with the size of ObjectManagementPackage in a buffer owned by the StateStream. The StateStream is defined as:

struct StateStream
{
    void *buffer;
    uint32_t capacity;
    uint32_t size;
};

capacity is the size of the memory pointed to by buffer, size is the current amount of bytes allocated from buffer.

The MessageHeader is defined as:

struct MessageHeader
{
    uint32_t type;
    uint32_t size;
    uint32_t data_offset;
};

The alloc_message function will first place the MessageHeader and then comes the data, some ASCII to the rescue:

+-------------------------------------------------------------------+
| MessageHeader | data                                              |
+-------------------------------------------------------------------+
<- data_offset ->
<-                          size                                   ->

The size and data_offset mentioned in the ASCII are two of the members of MessageHeader, these are assigned during the alloc_message call:

template<Class T>
void alloc_message(StateStream *state_stream, uint32_t type, T **data)
{
    uint32_t data_size = sizeof(T);

    uint32_t message_size = sizeof(MessageHeader) + data_size;

    // Allocate message and fill in the header.
    void *buffer = allocate(state_stream, message_size, alignof(MessageHeader));
    auto header = (MessageHeader*)buffer;

    header->type = type;
    header->size = message_size;
    header->data_offset = sizeof(MessageHeader);

    *data = memory_utilities::pointer_add(buffer, header->data_offset);
}

The buffer member of the StateStream will contain several consecutive chunks of message headers and data blocks.

+-----------------------------------------------------------------------+
| Header | data | Header | data | Header | data | Header | data | etc   |
+-----------------------------------------------------------------------+

This is the necessary code on the main thread to create an object and populate the StateStream which will later on be consumed by the render thread. A very similar pattern is used when changing the state of an object on the main thread, e.g:

void MeshObject::set_flags(renderable::Flags flags)
{
    _flags = flags;

    // Allocate a message on the `state_stream`.
    SetVisibilityPackage *svp;
    alloc_message(state_reflection->state_stream, MeshObject::SET_VISIBILITY, &svp);

    // Fill in message information.
    svp->object_type = RenderMeshObject::TYPE;

    // The render handle that got assigned in `WorldRenderInterface::create`
    // to be able to associate the main thread object with its render thread 
    // representation.
    svp->handle = render_handle;

    // The new flags value.
    svp->flags = _flags;
}

Getting the recorded state to the render thread

Let's take a step back and explain what happens in the main update loop during the following code excerpt:

// Flush state changes recorded on the `StateStream` for each world to
// the rendering world representation.
unsigned n_worlds = _worlds.size();
for (uint32_t i = 0; i < n_worlds; ++i) {
    auto &world = *_worlds[i];
    _render_interface->update_world(world);
}

When Lua has been creating, destroying, manipulating, etc objects during update() and is done, each world's StateStream which contains all the recorded changes is ready to be sent over to the render thread for consumption. The call to RenderInterface::update_world() will do just that, it roughly looks like:

void RenderInterface::update_world(World &world)
{
    UpdateWorldMsg uw;

    // Get the render thread representation of the `world`.
    uw.render_world = render_world_representation(world);

    // The world's current `state_stream` that contains all changes made 
    // on the main thread.
    uw.state_stream = world->_world_reflection_interface.state_stream;

    // Create and assign a new `state_stream` to the world's `_world_reflection_interface`
    // that will be used for the next frame.
    world->_world_reflection_interface->state_stream = new_state_stream();

    // Post a message to the render thread to update the world.
    post_message(UPDATE_WORLD, &uw);
}

This function will create a new message and post it to the render thread. The world being flushed and its StateStream are stored in the message and a new StateStream is created that will be used for the next frame. This new StateStream is set on the WorldRenderInterface of the World, and since all objects being created got a pointer to the same WorldRenderInterface they will use the newly created StateStream when storing state changes for the next frame.

Render thread

The render thread is spinning in a message loop:

void RenderInterface::render_thread_entry()
{
    while (!_quit) {
        // If there's no message -- put the thread to sleep until there's
        // a new message to consume.
        RenderMessage *message = get_message();

        void *data = data(message);
        switch (message->type) {
            case UPDATE_WORLD:
                internal_update_world((UpdateWorldMsg*)(data));
                break;

            // ... And a lot more case statements to handle different messages. There
            // are other threads than the main thread that also communicate with the
            // render thread. E.g., the resource loading happens on its own thread
            // and will post messages to the render thread.
        }
    }
}

The internal_update_world() function is defined as:

void RenderInterface::internal_update_world(UpdateWorldMsg *uw)
{
    // Call update on the `render_world` with the `state_stream` as argument.
    uw->render_world->update(uw->state_stream);

    // Release and recycle the `state_stream`.
    release_state_stream(uw->state_stream);
}

It calls update() on the RenderWorld with the StateStream and when that is done the StateStream is released to a pool.

void RenderWorld::update(StateStream *state_stream)
{
    MessageHeader *message_header;
    StatePackageHeader *package_header;

    // Consume a message and get the `message_header` and `package_header`.
    while (get_message(state_stream, &message_header, (void**)&package_header)) {
        switch (package_header->object_type) {
            case RenderWorld::TYPE:
            {
                auto omp = (WorldRenderInterface::ObjectManagementPackage*)package_header;
                // The call to `WorldRenderInterface::create` created this message.
                if (message_header->type == WorldRenderInterface::CREATE)
                    create_object(omp);
            }
            case (RenderMeshObject::TYPE)
            {
                if (message_header->type == MeshObject::SET_VISIBILITY) {
                    auto svp = (MeshObject::SetVisibilityPackage*>)package_header;

                    // The `render_handle` is used to do a lookup in `_objects_lut` to
                    // to get the `object_index`.
                    uint32_t object_index = _object_lut[package_header->render_handle];

                    // Get the `render_object`.
                    void *render_object = _objects[object_index];

                    // Cast it since the type is already given from the `object_type`
                    // in the `package_header`.
                    auto rmo = (RenderMeshObject*)render_object;

                    // Call update on the `RenderMeshObject`.
                    rmo->update(message_header->type, package_header);
                }
            }
            // ... And a lot more case statements to handle different kind of messages.
        }
    }
}

The above is mostly infrastructure to extract messages from the StateStream. It can be a bit involved since a lot of stuff is written out explicitly but the basic idea is hopefully simple and easy to understand.

On to the create_object call done when (message_header->type == WorldRenderInterface::CREATE) is satisfied:

void RenderWorld::create_object(WorldRenderInterface::ObjectManagementPackage *omp)
{
    // Acquire an `object_index`.
    uint32_t object_index = _objects.size();

    // Same recycling mechanism as seen for render handles.
    if (_free_object_indices.any()) {
        object_index = _free_object_indices.back();
        _free_object_indices.pop_back();
    } else {
        _objects.resize(object_index + 1);
        _object_types.resize(object_index + 1);
    }

    void *render_object = omp->user_data;
    if (omp->type == RenderMeshObject::TYPE) {
        // Cast the `render_object` to a `MeshObject`.
        RenderMeshObject *rmo = (RenderMeshObject*)render_object;

        // If needed, do more stuff with `rmo`.
    }

    // Store the `render_object` and `type`.
    _objects[object_index] = render_object;
    _object_types[object_index] = omp->type;

    if (omp->render_handle >= _object_lut.size())
        _object_lut.resize(omp->handle + 1);
    // The `render_handle` is used
    _object_lut[omp->render_handle] = object_index;
}

So the take away from the code above lies in the general usage of the render_handle and the object_index. The render_handle of objects are used to do a look up in _object_lut to get the object_index and type. Let's look at an example, the same RenderWorld::update code presented earlier but this time the focus is when the message is MeshObject::SET_VISIBILITY:

void RenderWorld::update(StateStream *state_stream)
{
    StateStream::MessageHeader *message_header;
    StatePackageHeader *package_header;

    while (get_message(state_stream, &message_header, (void**)&package_header)) {
        switch (package_header->object_type) {
            case (RenderMeshObject::TYPE)
            {
                if (message_header->type == MeshObject::SET_VISIBILITY) {
                    auto svp = (MeshObject::SetVisibilityPackage*>)package_header;

                    // The `render_handle` is used to do a lookup in `_objects_lut` to
                    // to get the `object_index`.
                    uint32_t object_index = _object_lut[package_header->render_handle];

                    // Get the `render_object` from the `object_index`.
                    void *render_object = _objects[object_index];

                    // Cast it since the type is already given from the `object_type`
                    // in the `package_header`.
                    auto rmo = (RenderMeshObject*)render_object;

                    // Call update on the `RenderMeshObject`.
                    rmo->update(message_header->type, svp);
                }
            }
        }
    }
}

The state reflection pattern shown in this post is a fundamental part of the engine. Similar patterns appear in other places as well and having a good understanding of this pattern makes it much easier to understand the internals of the engine.

135 comments:

  1. Good stuff! I believe there is a typo in the article, "The render thread objects inherit from RenderStateObject and the render thread objects inherit from RenderObject." I suppose it should be "The main thread objects inherit from RenderStateObject." And there is a little confusion if you choose RenderStateObject as base class name for main thread classes, because it is prefixed with Render. You mentioned all the classes in render thread being prefixed with Render. So someone may think RenderStateObject is a render thread class, and there should be a corresponding StateObject in main thread. And would you explain more about your sync mechanism between main thread and render thread?

    ReplyDelete
    Replies
    1. Hi Cherlix,

      Yes, that's correct it should be "The main thread". I've updated the post to that, thanks.

      Yeah, the naming might be a bit confusing, I agree. Hopefully it isn't too confusing to miss the overall message of the post.

      The way other threads communicate with the render thread is through the _render_interface. The _render_interface is basically just a thread safe ring buffer that other threads post messages to which the render thread consumes.

      The way the syncing happens between the main and render thread is by creating a fence and then later on waiting on that fence. Underneath the hood it's implemented as an "Event". On Windows that translates directly to creating a windows event, https://msdn.microsoft.com/en-ca/library/windows/desktop/ms682396(v=vs.85).aspx. So the way it works is that when the "create_fence" function is called from e.g. the main thread, a new event is acquired (there's a pool of them) and a message is posted to the render thread. When the render thread consumes the message it just sets the event which on Windows translates to "SetEvent". Calling "wait_for_fence" translates to "WaitForSingleObject" on Windows.

      Delete
  2. Great article! Just curious as to why the render_handle and _objects_lut were needed? Do you move RenderStateObjects around in memory or is there a reason why the render_handle couldn't just be the same as the RenderStateObject* and avoid the look-up in the render thread?

    Also, does the message-based approach offer other benefits over having a sync-point between the main and the renderer thread where you update any "dirty" objects' render state from their main/update state? It seems a little easier to filter out redundant state changes with the sync-point and possibly sync multiple objects in parallel. Though the message-based approach does keep slower operations from stalling the update thread.

    ReplyDelete
  3. Norton Security is the another name of the most recent and upgraded security programming suite ready to take a shot at countless like Microsoft Windows, Mac OS X, Android, iOS and numerous others.
    http://www.nortonsetup-norton.com

    ReplyDelete
  4. After purchasing office.com/setup need to visit office activate online to install and we provide technical services help in office setup on your Computer.
    http://officecom-officecom.com

    ReplyDelete
  5. Setup your norton Antivirus with help from norton setup , norton.com/setup after reaching the site go with the given steps. http://norton-nortoncom.com

    ReplyDelete
  6. Microsoft offers the on the web and furthermore the work zone interpretation of the working environment to its customers. You can clearly use the Office

    application without downloading the Office on your system. With the ultimate objective to get to the online adjustment of Office, you require a fast web

    affiliation and a program. You need to visit the association officecom-officecom.com/setup to use the online variation of the office setup

    ReplyDelete
  7. After purchasing office setup
    need to visit office activate online to install and we provide technical services help in office setup on your Computer. http://officecom-officecom.com

    ReplyDelete
  8. thanks for this information and if u want to know more about traveling packages kindly visit https://www.travelhunter.hu/

    ReplyDelete
  9. thanks for posting and for travel must visit last minute

    ReplyDelete
  10. www.OFFICE.COM/SETUP DOWNLOAD, INSTALLATION AND ACTIVATION FOR OFFICE SETUP. Microsoft Office suite contains reliably used ventures and organizations. The most used tasks of Microsoft Office are Word, a word taking care of program, Excel, a spreadsheet program, PowerPoint, a presentation maker, and Access, a database application
    http:// office-officeofficecom.com/office.com/setup

    ReplyDelete
  11. After visiting office.com/setup login to microsoft account to get hold of treaty of office setup, Any complexity in office.com/setup our fresh will assistance for office install
    http://http://officecom-office-office.com//
    office setup

    ReplyDelete
  12. Get the easiest way of setup and install Norton Setup with Product Key from office.com/setup. Norton proves to be one important aspects for all theinternet users to act as a shield against the increasing number of viruses, worms, spyware, and Trojan horses.http://norton-comnortoncom.com/
    office setup

    ReplyDelete
  13. BECOME A DIGITAL MARKETING
    EXPERT WITH US. Call Us For More Info. +91-9717 419 413
    COIM offers professional Digital Marketing Course Training in Delhi to help you for jobs and your business on the path to success.
    Digital Marketing Course in Laxmi Nagar
    Digital Marketing Institute in Delhi
    Digital Marketing training in Preet Vihar
    Online Digital Marketing Course in India
    Digital Marketing Institute in Delhi
    Digital Marketing Institute in Delhi

    ReplyDelete
  14. Delta Airlines Customer Service Number for :
    +1 (888) 262-3763
    Delta Airline Reservatations
    Airline Booking
    PNR Change
    Online Delta Ticket Booking
    Delta Customer Care
    Best phone number to reach Delta
    Airline VIP Lounge
    Delta Celebrity Reservation

    24 hours, 7 days
    Delta customer service number .
    Get in touch with Delta’s customer service department through the following phone numbers,email and contact form.
    For more information about reservations, technical support, cargo department, lost baggage and medallion status,
    please call the numbers listed Above .
    Delta Airlines Reservations Number


    Delta Customer Service
    Delta Customer Number
    Delta Service Number

    ReplyDelete
  15. BECOME A DIGITAL MARKETING
    EXPERT WITH US
    COIM offers professional Digital Marketing Course Training in Delhi to help you for job and your business on the path to success.
    +91-9717 419 413
    Digital Marketing Course in Laxmi Nagar
    Digital Marketing Institute in Delhi
    Digital Marketing training in Preet Vihar
    Online Digital Marketing Course in India
    Digital Marketing Institute in Delhi
    Digital Marketing Institute in Delhi



    ReplyDelete
  16. Apply Online Indian e Visa
    Apply Indian e Tourist Visa
    apply Indian eVisa South Africa
    Apply Indian eVisa Japan
    Apply Indian e Medical Visa
    Apply Indian e Business Visa
    Welcome To Indian Visa
    The e-Visa India application service is a program that allows travellers willing to travel to India to apply
    for their Visas online and at their comfort. Currently, thousands of travellers from more than 160 countries
    can apply for any e-Visa India or eTourist visa category based on the purpose of their visit and travel to
    India without having to visit embassies to get their Indian visa.
    for more Talk to expert | +91 997 119 5699
    mail us:-customer-care@indianvisa.online
    Apply Indian eVisa Japan

    ReplyDelete
  17. Blue world kanpur is a fantastic place in kanpur for enjoy with family or friends.



    The blue world kanpur water park goes for keeping up global guidelines in that capacity guests are required to facilitate with the specialists to keep the recreation center sheltered and agreeable.

    ReplyDelete
  18. Sign in to enter office setup product key. Know how to benefit, download, install, set in movement, uninstall and reinstall MS office setup.
    office.com/setup
    http://officecom-officeoffice.com/
    call us at +1-888-421-9666[tool free]

    ReplyDelete
  19. Amazing write up, never read such an informative blog and enjoyed it. Thankyou. Keep up the good work. Looking forward to read more.
    Shimla-Honeymoon-Packagese
    Kashmir honeymoon package
    Ladakh trip
    Manali Honeymoon Package
    online free travel leads|Buy Travel Leads|get free leads online

    ReplyDelete

  20. شركه عزل فوم بالجبيل
    لا تجهد نفسك ونحن تحت امرك ورهن اشارتك .
    أبرز خدمات مؤسسة الحرمــين للمقاولات العامة بالدمام والرياض

    شركه عزل فوم بالدمام

    شركه كشف تسربات المياه بالاحساء

    ReplyDelete

  21. شركه عزل فوم بالجبيل
    لا تجهد نفسك ونحن تحت امرك ورهن اشارتك .
    أبرز خدمات مؤسسة الحرمــين للمقاولات العامة بالدمام والرياض

    شركه عزل فوم بالدمام

    شركه كشف تسربات المياه بالاحساء

    ReplyDelete
  22. The Good
    First, let’s look at all that’s great about Putlocker and to be fair, there are many. First of all, as we mentioned above, the site allows you to access a truly impressive amount of movies and TV shows. From brand new episodes of the whatever the trending TV series is to quality movies from the Golden Era of Hollywood, you can find nearly anything on the website for more info click Putlocker hd

    You will certainly find new episodes of TV shows just a few hours after they’ve aired and in the case of movies, you can find them shortly after release, but they’re likely to be of inferior quality. For the HD versions, you would have to wait until they’ve had their initial Digital or DVD release.

    All the movies and TV show episodes also come with their own dedicated pages that have full details about the movie, including cast and crew, description and even ratings.

    Then there is also the fact that the website doesn’t require much from the user. If you want to watch a movie, you don’t need to go through a sign-up and verification process. There’s no usernames and there’s no passwords. And of course, there is also no payments required, which is the one defining feature that makes Putlocker such an attractive option for anyone looking to watch movies online.

    Putlocker also gives you a well-designed, user friendly user interface that can be navigated easily to find the movies and TV shows you want to watch. By working as a middleman between you and the cyberlocker where the content is stored, the website is also making your life easier because you don’t have to trawl through the internet to find that one episode of that one season of that one TV show you’re feeling like watching. You can just visit Putlocker and find what you want within a few clicks.

    The Putlocker movie player also comes with subtitles support so that whatever movie you’re watching, you can enjoy it with subtitles.

    ReplyDelete
  23. After reading your intro, I almost took every little note about the conservatives you described in your post. It's pretty good information and it's pleasure to read to gain more knowledge. Masters dissertation writing services

    ReplyDelete

  24. شركة تنظيف بالباحة التنظيف من الأشياء المهمة التي يجب الاهتمام بها فهناك العديد من الشركات التي تقوم بعمل التنظيف بشكل ممتاز كما تعمل بدقة كبيرة وكفاءة عالية.
    فيتم التنظيف بعدة طرق مختلفة ولكن تحتاج الى ايدي عاملة مثل شركة تنظيف بالباحة

    حتى يتم العمل على أفضل وجه فيجب القيام بالتنظيف باستخدام مواد التنظيف التي تتناسب مع كل ما تقوم بتنظيفه سواء كان سجاد او أثاث أو ستائر أو أرضيات أو غيرها من الاشياء الاخرى والاماكن الاخرى.
    فتحتاج عند القيام بالتنظيف إلى أدوات تعمل على التنظيف بشكل جيد وبطريقة جيدة تنظف بعمق ولكنها لاتتلف الاشياء الخاصة بالمنزل.
    كما يجب أيضا الاهتمام بالتعقيم للحمامات والمطابخ والتنظيف بدقة لأن تلك الاماكن أكثر تعرضا في بيتك او منزلك للاتساخات بشكل كبير شركة تنظيف بالباحة
    وتحتاج إلي التنظيف بشكل مستمر .
    كما أن هناك أيضا العديد من الاشخاص الذين يعانون من الاتساخات الموجودة بالاثاث والكنب بمختلف أنواعه فلكل نوع من أنواع الكنب طريقة مختلفة عن الاخري في التنظيف سواء كان من الجلد أو قماش أو مغطى بالخشب فيجب تحديد نوع المنظف الذي تقوم باستخدامه في التنظيف قبل الاستخدام حتى لا يتسبب في أي ضرر للكنب أو للاثاث.
    لا تقتصر عمليات التنظيف علي مكان محدد دون الآخر بل إنها من الأمور الهامه لجميع الأماكن سواء كانت منازل أو فلل أو شقق وايضا جميع أنواع الاثاث وخاصه السجاد والانتريهات الذي تتراكم فيه الاوساخ والأتربة والروائح الكريهه.
    كما يجب أن تتم عملية التنظيف للخزان أيضا والذي يعتبر من أهم الأغراض في كل منزل، ومن أهم أساليب النظافة أن يتم مراعاه التنظيف الجيد لمواسير الصرف والمجاري التي تعتبر من أكثر الأماكن التي تتعرض للانسداد وتراكم الحشرات بها وتتسبب في الأمراض.
    كما يجب أن تتضمن عملية التنظيف أيضا التخلص من القاذورات بشكل مستمر وعدم تراكمها في المنزل فهي من أهم أسباب انتشار الحشرات والروائح الكريهه والتي هي مصدر للأمراض المختلفه.
    النظافة من الأمور الضرورية والتي يحثنا عليها الدين الإسلامي وتتطلب تنفيذ دقيق واتباع الخطوات التي توفر اعلي نسب حمايه ممكنه والتخلص من مشاكل المنازل التي تنتج عن انتشار هذه الاوساخ المزعجه.
    شركة عزل أسطح
    من المعروف ان عزل الأسطح من المهام التي من الممكن ان تقوم بيه شركة عزل أسطح فهي من المهام التي من الممكن ان تساهم بشكل كبير في حماية الأسطح شركة عزل اسطح بالباحة
    وهذا من الكثير من العوامل الضارة والتي من الممكن ان تؤثر على الأسطح

    ReplyDelete
  25. normally I do not like to read blogs but your blog has a different vibe

    yellowatone hoodie coat



    ReplyDelete
  26. Due to the threats of potential hackers and data thieves, Google is now more concerned about the security of android users than ever before. To protect its users, Google has added the Factory reset protection (FRP) feature. Google Account Manager APK app can be extremely helpful in case you can’t remember the Google login credential and need to reset the android device immediately. All you have to do is install the Google Account Manager APK and you no longer need to bother about the FRP. In case the application is not working proper and ou want instant support from technicians then call at Google Telefoonnummer. who have proper knowldege and they will provide you a best solution as per your issue, they are available 24/7.

    ReplyDelete
  27. click on one of the sites below to get a variety of the best tips and tricks in life.

    pengeluaran bullseye

    ReplyDelete
  28. I really enjoy every part and have bookmarked you to see the new things you post. Well done for this excellent article. Please keep this work of the same quality.Recover Forgot Xfinity Email Password

    ReplyDelete
  29. Like!! Really appreciate you sharing this blog post.Really thank you! Keep writing. 온라인카지노

    ReplyDelete
  30. 온라인카지노 Hi, happy that i saw on this in google. Thanks!

    ReplyDelete
  31. 스포츠토토 information and also style, which includes a incredibly bendable topography.




    Thanks for the post and effort! Please keep sharing more such blog.

    ReplyDelete
  32. 스포츠토토 Thanks for sharing your info. I truly appreciate your efforts and I am waiting for your next post thanks once again.

    ReplyDelete
  33. خرید شال و روسری از مانلی ارائه دهنده انواع شال و روسری زنانه. این فروشگاه اینترنتی با در دست داشتن انواع مدل ها و جنس های مختلف شال و روسری مفتخر است که توانسته رضایت مشتریان خود را جلب نماید.

    ReplyDelete
  34. Hi there, just became alert to your blog through Google,
    and found that it's truly informative. 카지노사이트

    ReplyDelete
  35. Excellent Blog! I would like to thank you for the efforts you have made in writing this post.
    바카라사이트

    ReplyDelete
  36. All your hard work is much appreciated. This content data gives truly quality and unique information. I’m definitely going to look into it. Really very beneficial tips are provided here and, Thank you so much. Keep up the good works.
    온라인카지노

    ReplyDelete
  37. Thank you for sharing this information. I read your blog and I can't stop my self to read your full blog. Again Thanks and Best of luck to your next Blog in future.
    스포츠토토

    ReplyDelete
  38. When your website or blog goes live for the first time, it is exciting. 파워볼게임

    ReplyDelete
  39. I am always searching online for posts that can aid me. Thanks! 경마

    ReplyDelete
  40. Some genuinely nice and utilitarian info on this internet site, also I believe the style has great features. 슬롯머신

    v

    ReplyDelete
  41. Such a great blog.Thanks for sharing......... 토토

    ReplyDelete
  42. Excellent read, I just passed this onto a friend who was doing a little research on that. And he actually bought me lunch as I found it for him smile Therefore let me rephrase that: Thank you for lunch. 메이저사이트

    ReplyDelete
  43. Thank you for another informative blog. Where else could I get that kind of info written in such a perfect way?

    스포츠토토

    ReplyDelete
  44. This is my very first time that I am visiting here and I’m truly pleasurable to see everything at one place. 더킹카지노

    ReplyDelete
  45. Captivating post. I Have Been contemplating about this issue, so an obligation of appreciation is all together to post. Completely cool post.It 's greatly extraordinarily OK and Useful post.Thanks 사설토토사이트

    ReplyDelete
  46. Incredible! This blog lo?ks exactly ?ike my o?d one!
    It’s on a totally d?fferent subject b?t it ?as pretty muc?
    t?e ?ame layout ?nd design. Outstanding choice ?f colors!
    안전토토사이트

    ReplyDelete
  47. I wonder why the other experts of this sector don’t notice this. You must continue your writing. I’m sure, you’ve a huge readers’ base already! want to learn more? click the link here: 카지노사이트

    ReplyDelete
  48. Wonderful website. Plenty of helpful information here. I?¦m sending it to a few buddies ans also sharing in delicious. And certainly, thank you in your sweat! 온라인카지노

    ReplyDelete
  49. That's a great article! The neatly organized content is good to see. Can I quote a blog and write it on my blog? My blog has a variety of communities including these articles. Would you like to visit me later? keo nhacai


    ReplyDelete
  50. You made some good points there. I did a Google search about the topic and found most people will believe your blog. 메이저사이트

    ReplyDelete
  51. Very interesting points you have observed, thanks for putting up. 바카라사이트

    ReplyDelete
  52. Youre so right. Im there with you. Your weblog is definitely worth a read if anyone comes throughout it. Im lucky I did because now Ive received a whole new view of this. 오공슬롯"


    ReplyDelete
  53. How can you think of this? I thought about this, but I couldn't solve it as well as you.안전놀이터I am so amazing and cool you are. I think you will help me. I hope you can help me.


    ReplyDelete

  54. Greetings! Very helpful advice on this article! It is the little changes that make the biggest changes. Thanks a lot for sharing!

    카지노사이트

    ReplyDelete
  55. Excellent article. The writing style which you have used in this article is very good and it made the article of better quality.
    Topgun Jackets


    ReplyDelete
  56. Thanks for this vital information through your website. I would like to thank you for the efforts you had made for writing this awesome article.
    온라인카지노

    ReplyDelete
  57. Thanks For Sharing All of the Useful info I find this site to be very informative and useful will come back to read more. 스포츠토토

    ReplyDelete
  58. Thanks for sharing such great information. I also write about casino& sports betting online on my personal blog. kindly check out my page and learn more about online gambling! Thanks a lot admin! 바카라사이트

    ReplyDelete
  59. This Is Really Great Work. Thank You For Sharing Such A Useful Information Here In The Blog! Wanna find out more about online casino and sports betting? Just click the link below And your time worth while! Thankyou. 파워볼

    ReplyDelete
  60. Thanks for sharing this Blog with us. This blog is very interesting if want more information topic about sports betting & online casino Compassionately do check my website. 카지노사이트

    ReplyDelete
  61. Excellent write-up. I absolutely appreciate this website. 스포츠토토

    ReplyDelete
  62. Nice information, you write very nice articles, I visit your website for regular updates. 경마사이트

    ReplyDelete
  63. Thanks for sharing such great information. It was really helpful to me. 슬롯머신

    ReplyDelete
  64. Great blog! I am loving it!! Will come back again. 토토

    ReplyDelete
  65. I will truly value the essayist's decision for picking this fantastic article fitting to my matter. 카지노사이트존

    ReplyDelete
  66. Extremely instructive post ! There is a great deal of data here that can enable any business to begin with a fruitful interpersonal interaction campaign. 카지노사이트

    ReplyDelete
  67. What a truly magnificent post this is. Genuinely, outstanding amongst other presents I've ever seen on find in as long as I can remember. Amazing! 온라인카지노

    ReplyDelete
  68. Your article has answered the question I was wondering about! I would like to write a thesis on this subject, but I would like you to give your opinion once :D 꽁머니

    ReplyDelete
  69. I am a 슬롯사이트 expert. I've read a lot of articles, but I'm the first person to understand as well as you. I leave a post for the first time. It's great!!

    ReplyDelete
  70. Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic. If possible, as you gain expertise, would you mind updating your blog with extra information? It is extremely helpful for me. 토토사이트

    ReplyDelete
  71. This is my first time visit here. From the tons of comments on your articles,I guess I am not only one having all the enjoyment right here! 바카라사이트

    ReplyDelete
  72. Thanks for posting this info. I just want to let you know that I just check out your site and I find it very interesting and informative. I can't wait to read lots of your posts. seo berater finden

    ReplyDelete
  73. Keep up the wonderful piece of work, I read few posts on this internet site and I think that your blog is really interesting and holds bands of fantastic information.
    ทางเข้าเล่น igoal

    ReplyDelete
  74. But some research shows that speaking nicely to plants will support their growth, whereas yelling at them won't.Plants suitable for karachi Rather than the meaning of words, however, this may have more to do with vibrations and volume.

    ReplyDelete
  75. "In the wake of a few blog segments on your site, I truly like your scattering content to a blog limits. I saved it 토토사이트
    to my standard site list and will check again soon
    In the event that it's not all that aggregate bother, moreover visit my site and let me know your point of view.
    "

    ReplyDelete
  76. I was impressed by your writing. Your writing is impressive. I want to write like you.안전놀이터 I hope you can read my post and let me know what to modify. My writing is in I would like you to visit my blog.

    ReplyDelete
  77. This is a very interesting post. Thank you for posting a lot of interesting posts. And please visit my site!~! The name of the site is 슬롯사이트 .

    ReplyDelete
  78. I've been troubled for several days with this topic. 바카라사이트, But by chance looking at your post solved my problem! I will leave my blog, so when would you like to visit it?

    ReplyDelete
  79. Great blog article. Really looking forward to read more. 바카라사이트탑

    ReplyDelete
  80. Hi, this weekend is fastidious designed for me, since this
    occasion i am reading this impressive educational piece
    of writing here at my home 스포츠토토존

    ReplyDelete
  81. I finally found great post here. Thanks for the information. Please keep sharing more articles. 바카라사이트비즈

    ReplyDelete
  82. I’m thinking some of my readers might find a bit of this interesting. Do you mind if I post a clip from this and link back? Thanks 사설토토

    ReplyDelete
  83. I am always searching online for articles that can help me.
    야설

    ReplyDelete
  84. We are looking for a lot of data on this item. In the meantime, this is the perfect article I was looking for . Please post a lot about items related to 바카라사이트 !!! I am waiting for your article. And when you are having difficulty writing articles, I think you can get a lot of help by visiting my .

    ReplyDelete
  85. Excellent read, I just passed this onto a friend who was doing a little research on that. And he actually bought me lunch as I found it for him smile Therefore let me rephrase that: Thank you for lunch. 먹튀신고

    ReplyDelete
  86. Your post is very interesting to me. Reading was so much fun. I think the reason reading is fun is because it is a post related to that I am interested in. Articles related to 메이저사이트순위 you are the best. I would like you to write a similar post about !

    ReplyDelete
  87. This is the first time I read such type of blogs, Its really useful for the users. Keep updated This type of blogs provide information to the users.


    바카라사이트
    바카라
    바카라게임사이트


    ReplyDelete
  88. 스포츠토토티비
    스포츠중계
    토토사이트


    Very informative and trustworthy blog that was really helpful. I will bookmark your site for future use.

    ReplyDelete
  89. Your writing is perfect and complete. 크레이지슬롯 However, I think it will be more wonderful if your post includes additional topics that I am thinking of. I have a lot of posts on my site similar to your topic. Would you like to visit once?

    ReplyDelete
  90. Hello, I am one of the most impressed people in your article. 토토사이트순위 I'm very curious about how you write such a good article. Are you an expert on this subject? I think so. Thank you again for allowing me to read these posts, and have a nice day today. Thank you.

    ReplyDelete
  91. I have to show my respect for your kindness in support of visitors who have the need for assistance with this important theme. Your real commitment to passing the solution up and down had been remarkably beneficial and have specifically made associates like me to arrive at their pursuits. Your important guidelines means a whole lot a person like me and substantially more to my office colleagues. Warm regards; from all of us. I simply needed to thank you so much once more. I’m not certain what I might have done without these solutions provided by you over such situation. It had been a real terrifying setting in my circumstances, however , understanding the skilled manner you dealt with it forced me to weep over contentment. Now i’m happier for the support and pray you know what a great job that you are doing educating most people all through your web blog. I am sure you’ve never got to know any of us. Thanks for sharing, this is a fantastic article.Thanks Again. Great. 토토꽁머니

    ReplyDelete
  92. Unique information you provide us thanks for this -norton.com/nu16 When it comes to antivirus and security applications for their computers, many users choose Norton Security. Norton is a well-known security company that has been collaborated by many big brands for more security. You must have seen the name of the Norton offering security to the websites and more. Such a beautiful post thanks for sharing-McAfee.com/Activate is a comprehensive, and intelligent cybersecurity suite that safeguards you, your data, identity, privacy and all your devices at all times. It gives you the advantage of protecting all the devices of a household with a single subscription. It seamlessly integrates “antivirus, privacy, and identity” tools and features that are capable to crush the most advanced security threats. 사설토토

    ReplyDelete
  93. Wow! Thank you! I permanently needed to write on my blog something like that. Can I implement a part of your post to my blog? With havin so much written content do you ever run into any issues of plagorism or copyright infringement? My blog has a lot of completely unique content I’ve either written myself or outsourced but it appears a lot of it is popping it up all over the internet without my authorization. Do you know any ways to help reduce content from being ripped off? I’d truly appreciate it. Excellent beat ! I would like to apprentice while you amend your website, how can i subscribe for a blog site? The account aided me a acceptable deal. I had been tiny bit acquainted of this your broadcast offered bright clear concept 안전놀이터

    ReplyDelete
  94. I wish to show some appreciation to this writer just for rescuing me from this type of crisis. After researching throughout the search engines and getting methods which were not pleasant, I assumed my entire life was over. Living without the presence of approaches to the problems you’ve sorted out as a result of this report is a crucial case, as well as the kind that might have badly affected my entire career if I hadn’t discovered the blog. Your primary talents and kindness in playing with every aspect was helpful. I don’t know what I would have done if I had not come across such a step like this. I can at this moment relish my future. Thanks a lot so much for the high quality and results-oriented help. I will not think twice to recommend your blog post to any person who should receive support on this topic. Thanks a lot for giving everyone remarkably splendid chance to check tips from this website. It’s always so nice and packed with a lot of fun for me personally and my office fellow workers to search your blog nearly 3 times per week to read through the new issues you have. And of course, I’m also at all times astounded with the awesome advice you serve. Selected 4 areas on this page are really the most impressive I have had. 안전놀이터

    ReplyDelete
  95. Good day I am so delighted I found your weblog, I really found you by accident, while I was researching on Digg for something else, Nonetheless I am here now and would just like to say many thanks for a remarkable post and a all round interesting blog (I also love the theme/design), I donít have time to read through it all at the minute but I have bookmarked it and also added your RSS feeds, so when I have time I will be back to read a great deal more, Please do keep up the fantastic job. Hey I know this is off topic but I was wondering if you knew of any widgets I could add to my blog that automatically tweet my newest twitter updates. I’ve been looking for a plug-in like this for quite some time and was hoping maybe you would have some experience with something like this. Please let me know if you run into anything. I truly enjoy reading your blog and I look forward to your new updates.| This is a great blog. 먹튀검증

    ReplyDelete
  96. Wonderful blog! Do you have any tips and hints for aspiring writers? Because I’m going to start my website soon, but I’m a little lost on everything. Many thanks! Excellent to be visiting your blog again, it has been months for me. Rightly, this article that I've been served for therefore long. I want this article to finish my assignment within the faculty, and it has the same topic together with your article. Thanks for the ton of valuable help, nice share. 먹튀검증

    ReplyDelete
  97. I am constantly surprised by the amount of information accessible on this subject. What you presented was well researched and well written to get your stand on this over to all your readers. Thanks a lot my dear. I think this is an informative post and it is very beneficial and knowledgeable. Therefore, I would like to thank you for the endeavors that you have made in writing this article. All the content is absolutely well-researched. Thanks... 토토사이트

    ReplyDelete
  98. You have performed a great job on this article. It’s very precise and highly qualitative. You have even managed to make it readable and easy to read. You have some real writing talent. Thank you so much. Nice information, valuable and excellent design, as share good stuff with good ideas and concepts. I always prefer to such type of blog which provides some latest info. 먹튀검증

    ReplyDelete
  99. Wow, What an Outstanding post. I found this too much informatics. It is what I was seeking for. I would like to recommend you that please keep sharing such type of info.If possible, Thanks. Thanks for an interesting blog. What else may I get that sort of info written in such a perfect approach? I have an undertaking that I am just now operating on, and I have been on the lookout for such info. Whenever I have some free time, I visit blogs to get some useful info. Today, I found your blog with the help of Google. Believe me; I found it one of the most informative blog. 먹튀검증

    ReplyDelete
  100. I've been searching for hours on this topic and finally found your post. , I have read your post and I am very impressed. We prefer your opinion and will visit this site frequently to refer to your opinion. When would you like to visit my site? Really appreciate this wonderful post that you have provided for us.Great site and a great topic as well i really get amazed to read this. Its really good. A very awesome blog post. We are really grateful for your blog post. You will find a lot of approaches after visiting your post. I was exactly searching for. Thanks for such post 다이사이

    ReplyDelete
  101. First off I would like to say great blog! I had a quick question that I’d like to ask if you don’t mind. I was interested to find out how you center yourself and clear your mind before writing. I have had a hard time clearing my mind in getting my thoughts out. I do take pleasure in writing however it just seems like the first 10 to 15 minutes tend to be lost just trying to figure out how to begin. Any recommendations or hints? Thank you! Hi! Would you mind if I share your blog with my twitter group? There’s a lot of folks that I think would really enjoy your content. Please let me know. Many thanks 먹튀검증

    ReplyDelete
  102. Hello! This post couldn’t be written any better! Reading this post reminds me of my previous room mate! He always kept chatting about this. I will forward this article to him. Fairly certain he will have a good read. Thanks for sharing! This is the right blog for anyone who wants to find out about this topic. You realize so much its almost hard to argue with you (not that I actually would want…HaHa). You definitely put a new spin on a topic thats been written about for years. Great stuff, just great! 먹튀보증업체

    ReplyDelete
  103. Really no matter if someone doesn't be aware of after that its up to other users that they will help, so here it takes place 스포츠토토사이트.

    ReplyDelete
  104. Do you like the kind of articles related to 바카라사이트 If someone asks, they'll say they like related articles like yours. I think the same thing. Related articles are you the best.

    ReplyDelete
  105. Hello! Nice to meet you, I say . The name of the community I run is 안전놀이터추천, and the community I run contains articles similar to your blog. If you have time, I would be very grateful if you visit my site .

    ReplyDelete
  106. Thanks for this vital information through your website. I would like to thank you for the efforts you had made for writing this awesome article. products and services of retail banking, aeps cash withdrawal, digital banking services

    ReplyDelete
  107. This is a great inspiring article. I'm pretty much pleased with your good work. You put really very helpful information. Keep it up. Keep blogging. Looking forward to reading your next post and visit us if you want to know about online shop for flowers.

    ReplyDelete
  108. I'm very curious about how you write such a good article. Are you an expert on this subject? I think so. Thank you again for allowing me to read these posts, and have a nice day today. Thank you. 슬롯커뮤니티

    ReplyDelete