-
Notifications
You must be signed in to change notification settings - Fork 245
refactor(ww3d2): Introduce IRenderBackend interface #2613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a4d710d
e4a8981
b6dc7bd
3d80c67
9db0eb6
294ce4f
b157428
3568db1
0644f79
89ba40d
97a6034
2422827
4d2aeeb
8acd5f0
bdd6d93
2bbc25f
c03cc18
eeb27db
4760d65
93ae331
77b2768
74becc6
830cca1
fac1a75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| /* | ||
| ** Command & Conquer Generals Zero Hour(tm) | ||
| ** Copyright 2026 TheSuperHackers | ||
| ** | ||
| ** This program is free software: you can redistribute it and/or modify | ||
| ** it under the terms of the GNU General Public License as published by | ||
| ** the Free Software Foundation, either version 3 of the License, or | ||
| ** (at your option) any later version. | ||
| ** | ||
| ** This program is distributed in the hope that it will be useful, | ||
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| ** GNU General Public License for more details. | ||
| ** | ||
| ** You should have received a copy of the GNU General Public License | ||
| ** along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| // TheSuperHackers @refactor bobtista 10/04/2026 DX8Backend forwarding adapter. | ||
| // Every method in this file is a one-line trampoline to the existing | ||
| // DX8Wrapper static API. Keep it that way — if behavior needs to change it | ||
| // should change in DX8Wrapper, not here. | ||
|
|
||
| #include "DX8Backend.h" | ||
| #include "RenderBackend.h" | ||
|
|
||
| #include "WW3D2/dx8wrapper.h" | ||
| #include "WWMath/vector3.h" | ||
| #include "WW3D2/lightenvironment.h" | ||
|
|
||
| DX8Backend::DX8Backend() | ||
| { | ||
| } | ||
|
|
||
| DX8Backend::~DX8Backend() | ||
| { | ||
| } | ||
|
|
||
| IRenderBackend *Create_Render_Backend() | ||
| { | ||
| return new DX8Backend(); | ||
| } | ||
|
|
||
| void DX8Backend::Set_Gamma(float gamma, float bright, float contrast, bool calibrate, bool uselimit) | ||
| { | ||
| DX8Wrapper::Set_Gamma(gamma, bright, contrast, calibrate, uselimit); | ||
| } | ||
|
|
||
| void DX8Backend::Begin_Scene() | ||
| { | ||
| DX8Wrapper::Begin_Scene(); | ||
| } | ||
|
|
||
| void DX8Backend::End_Scene(bool flip_frame) | ||
| { | ||
| DX8Wrapper::End_Scene(flip_frame); | ||
| } | ||
|
|
||
| void DX8Backend::Flip_To_Primary() | ||
| { | ||
| DX8Wrapper::Flip_To_Primary(); | ||
| } | ||
|
|
||
| void DX8Backend::Clear(bool clear_color, bool clear_z_stencil, | ||
| const Vector3 & color, | ||
| float dest_alpha, float z, unsigned int stencil) | ||
| { | ||
| DX8Wrapper::Clear(clear_color, clear_z_stencil, color, dest_alpha, z, stencil); | ||
| } | ||
|
|
||
| void DX8Backend::Set_Viewport(const RenderBackendViewport & viewport) | ||
| { | ||
| D3DVIEWPORT8 vp; | ||
| vp.X = viewport.x; | ||
| vp.Y = viewport.y; | ||
| vp.Width = viewport.width; | ||
| vp.Height = viewport.height; | ||
| vp.MinZ = viewport.min_z; | ||
| vp.MaxZ = viewport.max_z; | ||
| DX8Wrapper::Set_Viewport(&vp); | ||
| } | ||
|
|
||
| void DX8Backend::Invalidate_Cached_Render_States() | ||
| { | ||
| DX8Wrapper::Invalidate_Cached_Render_States(); | ||
| } | ||
|
|
||
| void DX8Backend::Set_Ambient(const Vector3 & color) | ||
| { | ||
| DX8Wrapper::Set_Ambient(color); | ||
| } | ||
|
|
||
| void DX8Backend::Set_Light_Environment(LightEnvironmentClass * light_env) | ||
| { | ||
| DX8Wrapper::Set_Light_Environment(light_env); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| /* | ||
| ** Command & Conquer Generals Zero Hour(tm) | ||
| ** Copyright 2026 TheSuperHackers | ||
| ** | ||
| ** This program is free software: you can redistribute it and/or modify | ||
| ** it under the terms of the GNU General Public License as published by | ||
| ** the Free Software Foundation, either version 3 of the License, or | ||
| ** (at your option) any later version. | ||
| ** | ||
| ** This program is distributed in the hope that it will be useful, | ||
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| ** GNU General Public License for more details. | ||
| ** | ||
| ** You should have received a copy of the GNU General Public License | ||
| ** along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| // TheSuperHackers @refactor bobtista 10/04/2026 DX8Backend is the reference | ||
| // implementation of IRenderBackend that forwards every virtual method to the | ||
| // existing DX8Wrapper static facade. Pure adaptation, no new rendering logic. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "WW3D2/IRenderBackend.h" | ||
|
|
||
| class DX8Backend : public IRenderBackend | ||
| { | ||
| public: | ||
| DX8Backend(); | ||
| virtual ~DX8Backend() override; | ||
|
|
||
| virtual void Set_Gamma(float gamma, float bright, float contrast, bool calibrate, bool uselimit) override; | ||
|
|
||
| virtual void Begin_Scene() override; | ||
| virtual void End_Scene(bool flip_frame) override; | ||
| virtual void Flip_To_Primary() override; | ||
| virtual void Clear(bool clear_color, bool clear_z_stencil, | ||
| const Vector3 & color, | ||
| float dest_alpha, float z, unsigned int stencil) override; | ||
| virtual void Set_Viewport(const RenderBackendViewport & viewport) override; | ||
| virtual void Invalidate_Cached_Render_States() override; | ||
|
|
||
| virtual void Set_Ambient(const Vector3 & color) override; | ||
| virtual void Set_Light_Environment(LightEnvironmentClass * light_env) override; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /* | ||
| ** Command & Conquer Generals Zero Hour(tm) | ||
| ** Copyright 2026 TheSuperHackers | ||
| ** | ||
| ** This program is free software: you can redistribute it and/or modify | ||
| ** it under the terms of the GNU General Public License as published by | ||
| ** the Free Software Foundation, either version 3 of the License, or | ||
| ** (at your option) any later version. | ||
| ** | ||
| ** This program is distributed in the hope that it will be useful, | ||
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| ** GNU General Public License for more details. | ||
| ** | ||
| ** You should have received a copy of the GNU General Public License | ||
| ** along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| // TheSuperHackers @refactor bobtista 10/04/2026 Backend selection seam. The | ||
| // build links exactly one backend implementation, and that implementation | ||
| // defines Create_Render_Backend. WW3D owns the instance it returns; use | ||
| // WW3D::Get_Render_Backend() to reach the active backend. | ||
|
|
||
| #pragma once | ||
|
|
||
| class IRenderBackend; | ||
|
|
||
| // Construct the backend selected by the build. Exactly one backend | ||
| // implementation must define this function. | ||
| IRenderBackend *Create_Render_Backend(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| /* | ||
| ** Command & Conquer Generals Zero Hour(tm) | ||
| ** Copyright 2026 TheSuperHackers | ||
| ** | ||
| ** This program is free software: you can redistribute it and/or modify | ||
| ** it under the terms of the GNU General Public License as published by | ||
| ** the Free Software Foundation, either version 3 of the License, or | ||
| ** (at your option) any later version. | ||
| ** | ||
| ** This program is distributed in the hope that it will be useful, | ||
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| ** GNU General Public License for more details. | ||
| ** | ||
| ** You should have received a copy of the GNU General Public License | ||
| ** along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| // TheSuperHackers @refactor bobtista 10/04/2026 Abstract W3D-facing rendering | ||
| // interface so WW3D2 rendering can be re-targeted to other backends while the | ||
| // existing DX8 path stays as the reference implementation. | ||
|
|
||
| #pragma once | ||
|
|
||
| // Forward declarations keep this header includable without pulling in the full | ||
| // WW3D2 header graph. All W3D types below are passed by pointer or reference. | ||
|
|
||
| class LightEnvironmentClass; | ||
| class Vector3; | ||
|
|
||
| struct RenderBackendViewport | ||
| { | ||
| unsigned int x; | ||
| unsigned int y; | ||
| unsigned int width; | ||
| unsigned int height; | ||
| float min_z; | ||
| float max_z; | ||
| }; | ||
|
|
||
| // A method appears here once a caller routes through it, not in anticipation of | ||
| // one. The set below is what current callers route through; the rest of the | ||
| // DX8Wrapper API stays reachable through DX8Wrapper's static methods until a | ||
| // caller migrates, at which point the method it needs moves here. | ||
| // | ||
| // Method names intentionally match the existing DX8Wrapper names so migrating a | ||
| // caller is a mechanical DX8Wrapper::X(...) -> Get_Render_Backend()->X(...) | ||
| // rewrite. | ||
|
|
||
| class IRenderBackend | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will we do with direct calls to DX8, for example
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This took a lot of doing, but has been worth it. In my run ahead branches for bgfx (still local), I have it so there are two classes, handled differently. The high-frequency render/texture-stage-state calls aren't re-exposed as raw D3DRS_/D3DTSS_; they route through a backend-neutral fixed-function state cache (keyed by the same ordinals) plus typed semantic setters, so a non-DX8 backend reads intent rather than D3D enums. The genuinely DX8-only entry points (_Get_D3D_Device8, Create_DX8*, raw SetRenderTarget) migrate to named high-level methods (Set_Render_Target_With_Z, a view-capture primitive, …); the irreducible cases e.g. hand-written water pixel-shader bytecode go behind a named-enum hatch (Create_Legacy_Pixel_Shader(kind)) rather than a raw device pointer. The end state has no _Get_D3D_Device8 left in the engine subsystems; the raw device stays inside DX8Backend. Pure DX8 diagnostics eg Get_DX8_Texture_Stage_State_Value_Name simply stay on DX8Wrapper, as they're debug-only and not part of the abstraction.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They stay on DX8Wrapper. DX8-only entry points aren't interface candidates. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the following functions? How will they be dealt with if not as part of the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I left it minimalistic here on purpose, the idea is that in future PRs we add each method as needed for a given backend eg bgfx. I'm also happy to add them here if that's better. Could split them into buckets like so:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I trimmed to the 11 methods WW3D and DX8Wrapper actually call. We can add methods once a caller routes through it. So for now, everything on your list stays on DX8Wrapper until something migrates it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will there be a change to cleanup DX8Wrapper? It looks like it contains a number of things that do not directly belong there, such as
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, would you rather that happen in this PR vs a separate one?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll do that as a separate PR, keeps this one reviewable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will void WW3D::Flip_To_Primary()
{
DX8Wrapper::Flip_To_Primary();
}
void WW3D::Set_Gamma(float gamma,float bright,float contrast,bool calibrate)
{
DX8Wrapper::Set_Gamma(gamma,bright,contrast,calibrate);
}
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes for the existing high-level WW3D API. Methods such as Flip_To_Primary and Set_Gamma should remain public WW3D entry points and delegate to the active backend once migrated. I don’t intend to mirror every IRenderBackend method on WW3D - lower-level WW3D2 code can use the backend interface directly. I’m leaving that rewiring out of this skeleton PR because WW3D still exists separately under Generals and GeneralsMD rather than in Core. |
||
| { | ||
| public: | ||
| virtual ~IRenderBackend() {} | ||
|
|
||
| virtual void Set_Gamma(float gamma, float bright, float contrast, bool calibrate, bool uselimit) = 0; | ||
|
|
||
| virtual void Begin_Scene() = 0; | ||
| virtual void End_Scene(bool flip_frame) = 0; | ||
| virtual void Flip_To_Primary() = 0; | ||
| virtual void Clear(bool clear_color, bool clear_z_stencil, | ||
| const Vector3 & color, | ||
| float dest_alpha, float z, unsigned int stencil) = 0; | ||
| virtual void Set_Viewport(const RenderBackendViewport & viewport) = 0; | ||
| virtual void Invalidate_Cached_Render_States() = 0; | ||
|
|
||
| virtual void Set_Ambient(const Vector3 & color) = 0; | ||
| virtual void Set_Light_Environment(LightEnvironmentClass * light_env) = 0; | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will we cover DX8Caps related stuff?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Capability queries become a set of narrow virtuals with safe defaults eg Supports_Texture_Format(WW3DFormat), Supports_Compressed_Textures(), Get_Texture_Limits(), Supports_Texture_Op(...), etc.
DX8Backend forwards each to DX8Wrapper::Get_Current_Caps(), so DX8Caps stays the reference implementation and is never exposed directly. The few sites that read raw D3DCAPS8 bitfields today (COLORWRITEENABLE in W3DScene/W3DVolumetricShadow, TextureOpCaps in shader.cpp) and the Voodoo3 vendor check get promoted to neutral predicates (Supports_Color_Write_Mask(), Supports_Texture_Op(), Is_Legacy_Voodoo3()) so shared engine code carries no D3D types or #ifdefs. I've kept these out of this scaffolding PR deliberately, each lands with the call-site it unblocks, but the shape is good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing queries caps through the backend yet, so nothing is on the interface. When a caller needs it, it comes across as narrow predicates forwarding to DX8Caps, not as exposed D3DCAPS8.