Skip to content

Add 4-View Foveated ISR Pipeline core components and example game integration#2

Draft
Copilot wants to merge 5 commits intomasterfrom
copilot/create-condensed-design-specification
Draft

Add 4-View Foveated ISR Pipeline core components and example game integration#2
Copilot wants to merge 5 commits intomasterfrom
copilot/create-condensed-design-specification

Conversation

Copy link

Copilot AI commented Jan 8, 2026

  • Analyze repository structure and understand existing patterns
  • Create foveated module folder structure: src/mods/foveated/
  • Implement StereoEmulator.hpp - manages 4 emulated views with projection/frustum data
  • Implement ViewInjector.hpp - hooks D3D12 viewport/RT calls to redirect to atlas
  • Implement FoveatedAtlas.hpp - double-height render target for 4-view output
  • Implement VisibilityCache.hpp - mega-frustum optimization for culling results
  • Create example game integration folder structure: src/games/ExampleUE/
  • Implement example game entry point: ExampleUEEntry.h/cpp
  • Implement example renderer module: ExampleUERendererModule.h/cpp
  • Implement example camera module: ExampleUECameraModule.h/cpp
  • Create example memory offsets file: memory/offsets.h
  • Run code review
  • Address code review feedback
  • Address PR review comments:
    • Frame lifecycle: Now reads engine frame counter directly via pattern scan
    • Atlas config: Changed to use percentage-based sizing from VR runtime dimensions (separate horizontal/vertical scales)
    • All 4 views now use same dimensions (2x2 grid layout)
  • Run CodeQL security check
Original prompt

I'll analyze the mutars/acsvr repository to understand the integration pattern, then provide a condensed, actionable design specification. Now I have a clear understanding of the acsvr integration pattern. Let me create a condensed, agent-friendly specification document with the example integration folder structure.


4-View Foveated ISR Pipeline - Design Specification

Requirements

Prerequisites

  • UE SDK headers generated (via UE4SS dump or manual RE)
  • vrframework as submodule/dependency
  • Game-specific memory offsets for hook targets

Folder Structure for Game Integration

src/games/{GameName}/
├── {GameName}Entry.h          // Mod entry point, inherits from Mod
├── {GameName}Entry. cpp
├── {GameName}RendererModule.h // Frame/render tick hooks
├── {GameName}RendererModule.cpp
├── {GameName}CameraModule.h   // View/projection matrix hooks
├── {GameName}CameraModule.cpp
├── memory/
│   └── offsets.h              // Game-specific pattern/offset definitions
└── sdk/
    └── {GameName}SDK.h        // Reversed/generated engine structs

Architecture (Flat)

D3D12Hook (vtable/pointer) --> ViewInjector --> FoveatedAtlas (double-height RT)
       |                            |                   |
       v                            v                   v
SetViewports/SetRTs         StereoEmulator(4 views)   VisibilityCache
                                   |
                                   v
                            ISR Draw Pairs:  A(0+1), B(2+3)

Atlas Layout:  [FovealL|FovealR] top, [PeriphL|PeriphR] bottom

Core Components

1. StereoEmulator

Manages 4 emulated views with projection/frustum data.

#pragma once
#include <array>
#include <glm/glm. hpp>
#include "mods/vr/runtimes/VRRuntime.hpp"

namespace foveated {

enum class ViewType : uint8_t {
    FOVEAL_LEFT_PRIMARY = 0, FOVEAL_RIGHT_SECONDARY = 1,
    PERIPHERAL_LEFT_PRIMARY = 2, PERIPHERAL_RIGHT_SECONDARY = 3
};

struct EmulatedView {
    glm::mat4 projection, view;
    glm:: vec4 frustumPlanes[6];
    D3D12_VIEWPORT viewport;
    float fovScale;
    ViewType type;
    uint32_t stereoPassMask; // 0x1=Primary, 0x2=Secondary
};

class StereoEmulator {
public:
    static StereoEmulator& get();
    void initialize(runtimes::VRRuntime* runtime);
    void beginFrame(int frameIndex);
    bool isStereoActive() const { return m_stereoActive; }
    const EmulatedView& getView(ViewType t) const { return m_views[(size_t)t]; }
    std::array<D3D12_VIEWPORT, 4> computeAtlasViewports(uint32_t w, uint32_t h) const;
    void configureFOV(float fovealDeg, float peripheralDeg);
private:
    void buildViewMatrices(int frame);
    void computeFrustumPlanes(EmulatedView& v);
    std::array<EmulatedView, 4> m_views;
    runtimes::VRRuntime* m_runtime = nullptr;
    bool m_stereoActive = false;
    float m_fovealFov = 40.f, m_peripheralFov = 110.f;
    float m_fovealScale = 1.f, m_peripheralScale = 0.5f;
};
} // namespace foveated

2. ViewInjector

Hooks D3D12 viewport/RT calls to redirect to atlas.

#pragma once
#include "StereoEmulator. hpp"
#include <d3d12.h>

namespace foveated {

class ViewInjector {
public: 
    static ViewInjector& get();
    void install(class D3D12Hook* hook);
    void setAtlasRT(ID3D12Resource* rt, D3D12_CPU_DESCRIPTOR_HANDLE rtv);
private:
    void onSetViewports(ID3D12GraphicsCommandList5* cmd, UINT num, const D3D12_VIEWPORT* vps);
    void onSetRenderTargets(ID3D12GraphicsCommandList5* cmd, UINT num,
        const D3D12_CPU_DESCRIPTOR_HANDLE* rtvs, BOOL single, D3D12_CPU_DESCRIPTOR_HANDLE* dsv);
    void injectFovealPair(ID3D12GraphicsCommandList5* cmd);
    void injectPeripheralPair(ID3D12GraphicsCommandList5* cmd);
    
    D3D12Hook* m_hook = nullptr;
    ID3D12Resource* m_atlasRT = nullptr;
    D3D12_CPU_DESCRIPTOR_HANDLE m_atlasRTV{};
    std::array<D3D12_VIEWPORT, 4> m_atlasViewports;
    bool m_isRenderingFoveal = false;
};
} // namespace foveated

3. FoveatedAtlas

Double-height render target for 4-view output.

#pragma once
#include <d3d12.h>
#include <wrl/client.h>

namespace foveated {

struct AtlasConfig {
    uint32_t fovealWidth = 1440, fovealHeight = 1600;
    uint32_t peripheralWidth = 720, peripheralHeight = 800;
    DXGI_FORMAT format = DXGI_FORMAT_R10G10B10A2_UNORM;
};

class FoveatedAtlas {
public:
    static FoveatedAtlas& get();
    bool initialize(ID3D12Device* dev, const AtlasConfig& cfg);
    void shutdown();
    ID3D12Resource* getTexture() const { return m_atlas. Get(); }
    D3D12_CPU_DESCRIPTOR_HANDLE getRTV() const { return m_rtv; }
    uint32_t getTotalWidth() const { return m_cfg.fovealWidth * 2; }
    uint32_t getTotalHeight() const { return m_cfg.fovealHeight + m_cfg.peripheralHeight; }
    void transitionToRT(ID3D12GraphicsCommandList* cmd);
    void transitionToSRV(ID3D12GraphicsCommandList...

</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/mutars/vrframework/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits January 8, 2026 21:50
…egration

Co-authored-by: mutars <4204406+mutars@users.noreply.github.com>
…direction, enhance documentation

Co-authored-by: mutars <4204406+mutars@users.noreply.github.com>
Copilot AI changed the title [WIP] Add condensed design specification for 4-View Foveated ISR Pipeline Add 4-View Foveated ISR Pipeline core components and example game integration Jan 8, 2026
Copilot AI requested a review from mutars January 8, 2026 21:54

if (g_framework->is_ready()) {
auto vr = VR::get();
vr->m_engine_frame_count++;
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot you need to dig more into frame lifecyle, and ideally you want to gram frame from underal engine directly, as this was done mainly for unknown engines, when you do that you won't need to worry if engine is one frame nehind or not

Copy link
Author

Choose a reason for hiding this comment

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

Updated in 2f203f1. The renderer module now reads the frame counter directly from the engine via memory::engineFrameCounterAddr() pattern scan. The frame is cached in onBeginFrame before the engine processes it, and used in onBeginRender for VR state updates. This eliminates the manual increment approach.

uint32_t fovealWidth{1440};
uint32_t fovealHeight{1600};
uint32_t peripheralWidth{720};
uint32_t peripheralHeight{800};
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot I want textures to be same size across peripheral/focus we will be operatin on %of render size reported by vr->get_width/heaith this % will be separate horizontal and vertical

Copy link
Author

Choose a reason for hiding this comment

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

Updated in 2f203f1. Changed AtlasConfig to use percentage-based sizing:

  • horizontalScale and verticalScale (0.0-1.0) control size relative to vr->get_hmd_width()/get_hmd_height()
  • baseWidth/baseHeight are set from VR runtime dimensions
  • All 4 views now have identical dimensions in a 2x2 grid layout (total: 2eyeWidth x 2eyeHeight)

Copilot AI and others added 2 commits January 8, 2026 23:52
Co-authored-by: mutars <4204406+mutars@users.noreply.github.com>
Co-authored-by: mutars <4204406+mutars@users.noreply.github.com>
Copilot AI requested a review from mutars January 8, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants