Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a4d710d
feat(ww3d2): add IRenderBackend abstract interface
bobtista Apr 10, 2026
e4a8981
feat(ww3d2): add DX8Backend adapter forwarding to DX8Wrapper statics
bobtista Apr 10, 2026
b6dc7bd
feat(ww3d2): add global g_renderBackend pointer and lifecycle wiring
bobtista Apr 10, 2026
3d80c67
fix(ww3d2): Update copyright headers to TheSuperHackers for new files
bobtista Apr 18, 2026
9db0eb6
refactor(ww3d2): trim redundant comments from render backend interface
bobtista May 31, 2026
294ce4f
refactor(ww3d2): move backend implementations into Backend subfolder
bobtista May 31, 2026
b157428
feat(ww3d2): add IRenderBackend Initialize and Shutdown lifecycle hooks
bobtista May 31, 2026
3568db1
build(ww3d2): sort Backend render sources alphabetically in CMakeLists
bobtista Jun 4, 2026
0644f79
fix(ww3d2): guard against null render backend in device dependent shu…
bobtista Jul 19, 2026
89ba40d
refactor(ww3d2): decouple TransformKind values from D3D transform sta…
bobtista Jul 19, 2026
97a6034
refactor(ww3d2): add override specifiers to DX8Backend declarations
bobtista Jul 19, 2026
2422827
chore(ww3d2): use qualified include paths in render backend files
bobtista Jul 19, 2026
4d2aeeb
fix(ww3d2): Use a fixed width type for render backend shader ids
bobtista Aug 7, 2026
8acd5f0
refactor(ww3d2): Widen render backend draw and index ranges to unsign…
bobtista Aug 7, 2026
bdd6d93
refactor(ww3d2): Remove the SurfaceClass back buffer accessor from th…
bobtista Aug 7, 2026
2bbc25f
refactor(ww3d2): Separate render backend object lifetime from device …
bobtista Aug 7, 2026
c03cc18
refactor(ww3d2): Select render backend through factory
bobtista Aug 12, 2026
eeb27db
refactor(ww3d2): Move render backend ownership into WW3D
bobtista Aug 18, 2026
4760d65
refactor(ww3d2): Route WW3D rendering calls through the render backend
bobtista Aug 18, 2026
93ae331
refactor(ww3d2): Trim the render backend interface to the methods cal…
bobtista Aug 18, 2026
77b2768
refactor(ww3d2): Keep gamma limiting internal to DX8 backend
bobtista Aug 18, 2026
74becc6
refactor(ww3d2): Preserve gamma limiting mode
bobtista Aug 19, 2026
830cca1
refactor(ww3d2): Drop the unused backend device lifecycle pair
bobtista Aug 21, 2026
fac1a75
refactor(ww3d2): Destroy the render backend in reverse creation order
bobtista Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.cpp
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);
}
46 changes: 46 additions & 0 deletions Core/Libraries/Source/WWVegas/WW3D2/Backend/DX8Backend.h
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;
};
30 changes: 30 additions & 0 deletions Core/Libraries/Source/WWVegas/WW3D2/Backend/RenderBackend.h
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();
4 changes: 4 additions & 0 deletions Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ set(WW3D2_SRC
#assetmgr.h
assetstatus.cpp
assetstatus.h
Backend/DX8Backend.cpp
Backend/DX8Backend.h
Backend/RenderBackend.h
bitmaphandler.cpp
bitmaphandler.h
bmp2d.cpp
Expand Down Expand Up @@ -90,6 +93,7 @@ set(WW3D2_SRC
htree.h
#htreemgr.cpp
#htreemgr.h
IRenderBackend.h
intersec.cpp
intersec.h
intersec.inl
Expand Down
68 changes: 68 additions & 0 deletions Core/Libraries/Source/WWVegas/WW3D2/IRenderBackend.h
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

Copy link
Copy Markdown

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?

Copy link
Copy Markdown
Author

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.

Copy link
Copy Markdown
Author

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will we do with direct calls to DX8, for example _Get_D3D_Device8 or Get_DX8_Texture_Stage_State_Value_Name ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They stay on DX8Wrapper. DX8-only entry points aren't interface candidates.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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 IRenderBackend? Does the IRenderBackend already claim to be complete or are the chosen function just the simple ones that can be abstracted so far?

SetCleanupHook
Is_Initted
Get_Format_Name
Get_Render_State
Set_Render_State
Release_Render_State
Get_Free_Texture_RAM
Begin_Statistics
End_Statistics
Get_Last_Frame_Statistics
Get_FrameCount
Get_Fog_Color
Convert_Color (looks like utility function to be moved elsewhere)
Clamp_Color (looks like utility function to be moved elsewhere)
Set_Alpha (looks like utility function to be moved elsewhere)
Create_Additional_Swap_Chain
Set_Render_Target
Apply_Default_State
Get_Vertex_Processing_Behavior
getBackBufferFormat
Reset_Device
Registry_Save_Render_Device
Registry_Load_Render_Device
Set_Draw_Polygon_Low_Bound_Limit

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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:

  • Will be promoted when a backend needs them: render-state, statistics, Reset_Device, Set_Render_Target (skeleton already has Set_Render_Target_With_Z + Create_Render_Target).
  • Stay DX8-specific (escape hatches on DX8Wrapper): _Get_D3D_Device8, format-name/state-name debug helpers, registry device save/load, Get_Vertex_Processing_Behavior.
  • Don't belong on a backend at all (utilities): Convert_Color, Clamp_Color, Set_Alpha — agree with him, separate cleanup.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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 Convert_Color, Clamp_Color.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that as a separate PR, keeps this one reviewable

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will IRenderBackend then also be served with static functions through WW3D class? It already does so for a number of 1 to 1 DX8Wrapper function calls, such as

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);
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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;
};
1 change: 1 addition & 0 deletions Core/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "dx8vertexbuffer.h"
#include "dx8indexbuffer.h"
#include "dx8renderer.h"
#include "IRenderBackend.h"
#include "ww3d.h"
#include "camera.h"
#include "WWLib/wwstring.h"
Expand Down
Loading
Loading