Jump to content

  • Log In with Google      Sign In   
  • Create Account


#ActualSandman

Posted 07 April 2013 - 01:53 PM

On the one side, I can somewhat safely use static_cast as long as my gfx implementation takes care that no wrong texture is passed in. On the other hand, this does kill the whole purpose of me trying to make the system SOLID, since one should not rely on my gfx implemenatation for things to work nicely.


I don't think you can avoid violating either liskov, dependency inversion, or both, with your current approach to dealing with resources.
As you say, you need to be able to access the underlying types in order to be able to use them for anything, which means either exposing them in the interface (violates both dependency inversion, and liskov) or doing some upcasting somewhere in your engine to get at higher level interfaces (violates liskov).

In any case, you'd kind of expect liskov to be violated anyway. You really can't expect a D3DEngine to work happily if you swap out half the textures with COpenGLTexture. It just plain wouldn't work, regardless of how it gets at the data inside.

The thing is, the details of those texture structures is fundamentally engine specific. And so if you want to satisfy the SOLID criteria, you need to hide these details behind the engine interface.

It seems to me that the way to do that is to have the api specific engine implementation worry about storing api specific texture resources. CTexture stops storing a texture, and instead becomes a resource locator. It can expose functions which act on the texture, and internally it implements that by making a call to the engine, which can look up the texture and perform the actual modification.


// templated for strong typing of resource types

template<class ResourceTypeT> 

class CResourceLocator

{ 

public:

    CResourceLocator(int hash);

    int GetHash() const;

}


typedef CResourceLocator<CTexture> TextureLocator;


class ITextureActionHandler :

{


    int GetWidth(const TextureLocator& textureLocator) const = 0;

    int GetHeight(const TextureLocator& textureLocator) const = 0;


   void SetPixel(const TextureLocator& textureLocator, int x, int y, CColour c) = 0;


}


class CTexture 

{

public:


    int GetWidth() const

    {

        m_pActionHandler->GetWidth(this);

    }

    int GetHeight() const

    {

        m_pActionHandler->GetHeight(this);

    }



   void SetPixel(int x, int y, CColour c)

   {

        m_pActionHandler->SetPixel(m_Locator, x, y, c);

   }


   const TextureLocator& GetLocator()

   {

       return m_Locator;

   }

private:

    TextureLocator m_Locator;

    ITextureActionHandler* m_pActionHandler; 

}



The action handler implementation could be the engine itself - it would then perform a lookup in some internal table of D3DTexture objects to find the specific texture implementation, and then perform the operation on it. The danger here though is that changing your implementation of the engine forces you to somehow notify all your texture instances. However if you created another layer of abstraction (a separate api agnostic handler, which then calls into the api specific implementation) you'd just need to update the intermediate handler to point to the correct engine instance. Then you could switch between D3D and OpenGL on the fly, and not worry about all the texture instances - they would just carry on working with the new engine, as they are now completely engine agnostic.

#3Sandman

Posted 07 April 2013 - 01:52 PM

On the one side, I can somewhat safely use static_cast as long as my gfx implementation takes care that no wrong texture is passed in. On the other hand, this does kill the whole purpose of me trying to make the system SOLID, since one should not rely on my gfx implemenatation for things to work nicely.


I don't think you can avoid violating either liskov, dependency inversion, or both, with your current approach to dealing with resources.
As you say, you need to be able to access the underlying types in order to be able to use them for anything, which means either exposing them in the interface (violates both dependency inversion, and liskov) or doing some upcasting somewhere in your engine to get at higher level interfaces (violates liskov).

In any case, you'd kind of expect liskov to be violated anyway. You really can't expect a D3DEngine to work happily if you swap out half the textures with COpenGLTexture. It just plain wouldn't work, regardless of how it gets at the data inside.

The thing is, the details of those texture structures is fundamentally engine specific. And so if you want to satisfy the SOLID criteria, you need to hide these details behind the engine interface.

It seems to me that the way to do that is to have the api specific engine implementation worry about storing api specific texture resources. CTexture stops storing a texture, and instead becomes a resource locator. It can expose functions which act on the texture, and internally it implements that by making a call to the engine, which can look up the texture and perform the actual modification.


// templated for strong typing of resource types

template<class ResourceTypeT> 

class CResourceLocator

{ 

public:

    CResourceLocator(int hash);

    int GetHash() const;

}


typedef CResourceLocator<CTexture> TextureLocator;


class ITextureActionHandler :

{


    int GetWidth(const TextureLocator& textureLocator) const = 0;

    int GetHeight(const TextureLocator& textureLocator) const = 0;


   void SetPixel(const TextureLocator& textureLocator, int x, int y, CColour c) = 0;


}


class CTexture 

{

public:


    int GetWidth() const

    {

        m_pActionHandler->GetWidth(this);

    }

    int GetHeight() const

    {

        m_pActionHandler->GetHeight(this);

    }



   void SetPixel(int x, int y, CColour c)

   {

        m_pActionHandler->SetPixel(m_Locator, x, y, c);

   }


   const TextureLocator& GetLocator()

   {

       return m_Locator;

   }

private:

    TextureLocator m_Locator;

    ITextureActionHandler* m_pActionHandler; 

}



The action handler implementation could be the engine itself - it would then perform a lookup in some internal table of D3DTexture objects to find the specific texture implementation, and then perform the operation on it. The danger here though is that changing your implementation of the engine forces you to somehow notify all your texture instances. However if you created another layer of abstraction (a separate api agnostic handler, which then calls into the api specific implementation) you'd just need to update the intermediate handler to point to the correct engine instance. Then you could switch between D3D and OpenGL on the fly, and not worry about all the texture instances - they would just carry on working with the new engine, as they are now completely engine agnostic.

#2Sandman

Posted 07 April 2013 - 06:21 AM

 On the one side, I can somewhat safely use static_cast as long as my gfx implementation takes care that no wrong texture is passed in. On the other hand, this does kill the whole purpose of me trying to make the system SOLID, since one should not rely on my gfx implemenatation for things to work nicely.

 

I don't think you can avoid violating either liskov, dependency inversion, or both, with your current approach to dealing with resources.

As you say, you need to be able to access the underlying types in order to be able to use them for anything, which means either exposing them in the interface (violates both dependency inversion, and liskov) or doing some upcasting somewhere in your engine to get at higher level interfaces (violates liskov).

 

In any case, you'd kind of expect liskov to be violated anyway. You really can't expect a D3DEngine to work happily if you swap out half the textures with COpenGLTexture. It just plain wouldn't work, regardless of how it gets at the data inside.

 

The thing is, the details of those texture structures is fundamentally engine specific. And so if you want to satisfy the SOLID criteria, you need to hide these details behind the engine interface.

 

It seems to me that the way to do that is to have the api specific engine implementation worry about storing api specific texture resources. CTexture stops storing a texture, and instead becomes a resource locator. It can expose functions which act on the texture, and internally it implements that by making a call to the engine, which can look up the texture and perform the actual modification.

 

// templated for strong typing of resource types
template<class ResourceTypeT> 
class CResourceLocator
{ 
public:
    CResourceLocator(int hash);
    int GetHash() const;
}
 
typedef CResourceLocator<CTexture> TextureLocator;
 
class ITextureActionHandler :
{
 
    int GetWidth(const TextureLocator& textureLocator) const = 0;
    int GetHeight(const TextureLocator& textureLocator) const = 0;
 
   void SetPixel(const TextureLocator& textureLocator, int x, int y, CColour c) = 0;
 
}
 
class CTexture : public ITextureAccessor, ITextureMutator
{
public:
 
    int GetWidth() const
    {
        m_pActionHandler->GetWidth(this);
    }
    int GetHeight() const
    {
        m_pActionHandler->GetHeight(this);
    }
 
 
   void SetPixel(int x, int y, CColour c)
   {
        m_pActionHandler->SetPixel(m_Locator, x, y, c);
   }
 
   const TextureLocator& GetLocator()
   {
       return m_Locator;
   }
private:
    TextureLocator m_Locator;
    ITextureActionHandler* m_pActionHandler; 
}

 

The action handler implementation could be the engine itself - it would then perform a lookup in some internal table of D3DTexture objects to find the specific texture implementation, and then perform the operation on it. The danger here though is that changing your implementation of the engine forces you to somehow notify all your texture instances. However if you created another layer of abstraction (a separate api agnostic handler, which then calls into the api specific implementation) you'd just need to update the intermediate handler to point to the correct engine instance. Then you could switch between D3D and OpenGL on the fly, and not worry about all the texture instances - they would just carry on working with the new engine, as they are now completely engine agnostic.


#1Sandman

Posted 07 April 2013 - 06:21 AM

 On the one side, I can somewhat safely use static_cast as long as my gfx implementation takes care that no wrong texture is passed in. On the other hand, this does kill the whole purpose of me trying to make the system SOLID, since one should not rely on my gfx implemenatation for things to work nicely.

 

I don't think you can avoid violating either liskov, dependency inversion, or both, with your current approach to dealing with resources.

As you say, you need to be able to access the underlying types in order to be able to use them for anything, which means either exposing them in the interface (violates both dependency inversion, and liskov) or doing some upcasting somewhere in your engine to get at higher level interfaces (violates liskov).

 

In any case, you'd kind of expect liskov to be violated anyway. You really can't expect a D3DEngine to work happily if you swap out half the textures with COpenGLTexture. It just plain wouldn't work, regardless of how it gets at the data inside.

 

The thing is, the details of those texture structures is fundamentally engine specific. And so if you want to satisfy the SOLID criteria, you need to hide these details behind the engine interface.

 

It seems to me that the way to do that is to have the api specific engine implementation worry about storing api specific texture resources. CTexture stops storing a texture, and instead becomes a resource locator. It can expose functions which act on the texture, and internally it implements that by making a call to the engine, which can look up the texture and perform the actual modification.

 

// templated for strong typing of resource types
template<class ResourceTypeT> 
class CResourceLocator
{ 
public:
    CResourceLocator(int hash);
    int GetHash() const;
}
 
typedef CResourceLocator<CTexture> TextureLocator;
 
class ITextureActionHandler :
{
 
    int GetWidth(const TextureLocator& textureLocator) const = 0;
    int GetHeight(const TextureLocator& textureLocator) const = 0;
 
   void SetPixel(const TextureLocator& textureLocator, int x, int y, CColour c) = 0;
 
}
 
class CTexture : public ITextureAccessor, ITextureMutator
{
public:
 
    int GetWidth() const
    {
        m_pActionHandler->GetWidth(this);
    }
    int GetHeight() const
    {
        m_pActionHandler->GetHeight(this);
    }
 
 
   void SetPixel(int x, int y, CColour c)
   {
        m_pActionHandler->SetPixel(m_Locator, x, y, c);
   }
 
   const TextureLocator& GetLocator()
   {
       return m_Locator;
   }
private:
    TextureLocator m_Locator;
    ITextureActionHandler* m_pActionHandler; 
}

 

The action handler implementation could be the engine itself - it would then perform a lookup in some internal table of D3DTexture objects to find the specific texture implementation, and then perform the operation on it. The danger here though is that changing your implementation of the engine forces you to somehow notify all your texture instances. However if you created another layer of abstraction (a separate api agnostic handler, which then calls into the api specific implementation) you'd just need to update the intermediate handler to point to the correct engine instance. Then you could switch between D3D and OpenGL on the fly, and not worry about all the texture instances - they would just carry on working with the new engine, as they are now completely engine agnostic.


PARTNERS