Skip to main content
added 113 characters in body
Source Link
user377672
user377672

From my standpoint, when you run into heavy resistance trying to dodesign things like this without leaking third-party details (or any details that you're uncomfortable leaking), you might be directing the bulk of dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future as you tackle more and more rendering targets and discover their unique capabilities and individual requirements.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation details you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared capabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine.

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious set of abstractions that are prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer (even ones you didn't anticipate in advance), build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, even have an implementation that outputs to the console, etc. And most of all it should allow you to focus on building higher-level abstractions where you don't get knee-deep in complex details while trying to design abstract interfaces that vary wildly from one concrete implementation to the next in ways that are extremely difficult to fully hide away.

From my standpoint, when you run into heavy resistance trying to do things like this without leaking third-party details, you might be directing the dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation details you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared capabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine.

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious set of abstractions that are prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer (even ones you didn't anticipate in advance), build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, even have an implementation that outputs to the console, etc. And most of all it should allow you to focus on building higher-level abstractions where you don't get knee-deep in complex details while trying to design abstract interfaces that vary wildly from one concrete implementation to the next in ways that are extremely difficult to fully hide away.

From my standpoint, when you run into heavy resistance trying to design things like this without leaking third-party details (or any details that you're uncomfortable leaking), you might be directing the bulk of dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future as you tackle more and more rendering targets and discover their unique capabilities and individual requirements.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation details you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared capabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine.

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious set of abstractions that are prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer (even ones you didn't anticipate in advance), build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, even have an implementation that outputs to the console, etc. And most of all it should allow you to focus on building higher-level abstractions where you don't get knee-deep in complex details while trying to design abstract interfaces that vary wildly from one concrete implementation to the next in ways that are extremely difficult to fully hide away.

added 82 characters in body
Source Link
user377672
user377672

From my standpoint, when you run into heavy resistance trying to do things like this without leaking third-party details, you might be directing the dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation designsdetails you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared capabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine.

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious abstraction that'sset of abstractions that are prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer (even ones you didn't anticipate in advance), build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, even have an implementation that outputs to the console, etc. And most of all it should allow you to focus on building higher-level abstractions where you don't get knee-deep in complex details while trying to design abstract interfaces that vary wildly from one concrete implementation to the next in ways that are extremely difficult to fully hide away.

From my standpoint, when you run into heavy resistance trying to do things like this without leaking third-party details, you might be directing the dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation designs you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared capabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine.

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious abstraction that's prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer, build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, etc.

From my standpoint, when you run into heavy resistance trying to do things like this without leaking third-party details, you might be directing the dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation details you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared capabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine.

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious set of abstractions that are prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer (even ones you didn't anticipate in advance), build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, even have an implementation that outputs to the console, etc. And most of all it should allow you to focus on building higher-level abstractions where you don't get knee-deep in complex details while trying to design abstract interfaces that vary wildly from one concrete implementation to the next in ways that are extremely difficult to fully hide away.

added 82 characters in body
Source Link
user377672
user377672

From my standpoint, when you run into heavy resistance trying to do things like this without leaking third-party details, you might be directing the dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation designs you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared abilitiescapabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine. 

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious abstraction that's prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer, build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, etc.

From my standpoint, when you run into heavy resistance trying to do things like this without leaking third-party details, you might be directing the dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right, in which case you might do something like this:

class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared abilities that you require in advance. Over time as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth. But at least you don't have to start out trying to build some super ambitious abstraction that's prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer, build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, etc.

From my standpoint, when you run into heavy resistance trying to do things like this without leaking third-party details, you might be directing the dependencies and designing the abstractions in the opposite direction of the path of least resistance.

For example, in your case, you are trying to abstract away all required rendering capabilities to allow non-intrusive replacement of the rendering backend and directing a whole lot of dependencies towards abstractions of rendering concepts. Yet is that really the path of least resistance? Unless your design requirements for rendering are very simple and easy to fully anticipate in advance, that could get explosively complex and involve repeated design iterations and cascading design breakages in the future.

What about the other way around? Can you reason more confidently about the design requirements on your end, absent third-party rendering requirements, and have your abstract interfaces for the logical components in your architecture (ex: game components) expose enough data so that they can be rendered (and possibly serialized, controlled by user input, etc) by anything? That might be so much easier to design and get right without getting tangled up in low-level implementation designs you get tempted to expose in public interfaces, in which case you might do something like this:

// Abstracts an entire renderer (not its individual rendering capabilities).
class IRenderer
{
public:
    virtual ~IRenderer() {}

    // Renders all the visible objects in the scene.
    virtual void render(const IScene& scene) = 0;
};

... where something like scene above provides everything any renderer will need to access in order to render the scene/world/etc. Then you might have an OpenGL renderer, SFML renderer, DirectX renderer, and you don't have to try to build abstractions that target the common-denominator aspects of all their shared capabilities that you require in advance among all third party libraries that you use or possibly could use in the future in your engine. 

Over time, as you implement multiple concrete renderers, you might find ways to reuse some code between them and so forth to simplify their implementation and make it easier to introduce new rendering targets. But at least you don't have to start out trying to build some super ambitious abstraction that's prone to leak details that could change in the future. This type of design also gives you tremendous breathing room to adapt to the unique capabilities of the renderer, build efficient caches to accelerate drawing under the hood without building abstractions over them, easily handle both immediate and retained mode designs, etc.

Source Link
user377672
user377672
Loading