#pragma once
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/rendering_device.hpp>
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/shader.hpp>
#include <godot_cpp/classes/shader_material.hpp>
#include <godot_cpp/classes/texture2drd.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/color.hpp>
#include <godot_cpp/variant/dictionary.hpp>
#include <godot_cpp/variant/packed_byte_array.hpp>
#include <godot_cpp/variant/rid.hpp>
#include <godot_cpp/variant/string.hpp>
#include <godot_cpp/variant/typed_array.hpp>
#include "page_table.h"
#include "tile_cache.h"
#include "tile_request_queue.h"
using namespace godot;
// ── SparseVirtualTexture node ────────────────────────────────────────────────
// Manages the SVT pipeline: page table, physical cache atlas, feedback analysis,
// and tile upload. Runs entirely on the global RenderingDevice.
class VirtualTexture : public Node {
GDCLASS(VirtualTexture, Node)
protected:
static void _bind_methods();
public:
VirtualTexture();
~VirtualTexture();
// ── Texture registration ────────────────────────────────────────────
int register_texture(const String &name, int width = 0, int height = 0);
void unregister_texture(int vt_id);
int get_texture_count() const;
// ── CPU image reconstruction ────────────────────────────────────────
Ref<Image> get_reconstructed_image(int vt_id) const;
TypedArray<Dictionary> get_all_textures() const;
// ── Invalidation (called by BlenderBridge on texture edits) ─────────
void invalidate_region(int texture_id, int tile_x, int tile_y, int w, int h, int mip);
void invalidate_all(int texture_id);
// ── Atlas / page table access (for materials) ───────────────────────
Ref<Texture2DRD> get_atlas_texture() const;
Ref<Texture2DRD> get_page_table_texture() const;
// ── Material factory ────────────────────────────────────────────────
Ref<ShaderMaterial> create_svt_material(const Color &fallback_albedo, float roughness, float metallic);
Ref<Shader> get_svt_shader() const;
// ── Configuration ───────────────────────────────────────────────────
void set_tile_size(int size);
int get_tile_size() const;
void set_tile_budget(int budget);
int get_tile_budget() const;
void set_cache_grid_size(int size);
int get_cache_grid_size() const;
void set_mip_levels(int levels);
int get_mip_levels() const;
// ── Feedback submission (called by FeedbackEffect or externally) ────
void submit_feedback(const PackedByteArray &feedback_data);
// ── Tile upload (raw RGBA8 pixel data for a single tile) ────────────
// Called by BlenderBridge or Blender addon after tile production.
Error upload_tile(int texture_id, int tile_x, int tile_y, int mip, const PackedByteArray &rgba_data);
// ── Frame processing ────────────────────────────────────────────────
void _ready() override;
void _process(double delta) override;
void _exit_tree() override;
// ── Stats ───────────────────────────────────────────────────────────
Dictionary get_stats() const;
// ── Signals ─────────────────────────────────────────────────────────
// tile_requested(texture_id: int, tile_x: int, tile_y: int, mip: int)
// tile_loaded(texture_id: int, tile_x: int, tile_y: int, mip: int)
private:
// ── Configuration ───────────────────────────────────────────────────
int _tile_size = 128; // pixels per tile edge
int _tile_budget = 16; // max tiles uploaded per frame
int _cache_grid_size = 32; // 32×32 = 1024 cache slots
int _mip_levels = 8;
// ── Core data structures ────────────────────────────────────────────
PageTable _page_table;
TileCache _tile_cache;
TileRequestQueue _request_queue;
// ── Texture registry ────────────────────────────────────────────────
struct RegisteredTexture {
int id;
String name;
bool active;
int width = 0; // Source image width in pixels
int height = 0; // Source image height in pixels
Ref<Image> cpu_image; // CPU-side reconstructed image
};
std::vector<RegisteredTexture> _textures;
int _next_texture_id = 1;
// ── GPU resources ───────────────────────────────────────────────────
RID _atlas_rid; // Physical cache atlas (RGBA8, tile_size * grid × tile_size * grid)
RID _page_table_rid; // Page table texture (RGBA8, one texel per virtual tile)
Ref<Texture2DRD> _atlas_tex;
Ref<Texture2DRD> _page_table_tex;
bool _gpu_initialized = false;
// ── Shader (lazy-loaded) ────────────────────────────────────────────
Ref<Shader> _svt_shader;
bool _shader_loaded = false;
// ── Frame tracking ──────────────────────────────────────────────────
uint64_t _frame_count = 0;
int _tiles_uploaded_this_frame = 0;
// ── GPU setup/teardown ──────────────────────────────────────────────
void _init_gpu_resources();
void _free_gpu_resources();
void _upload_page_table_to_gpu();
// ── Tile upload to atlas ────────────────────────────────────────────
void _upload_tile_to_atlas(PhysicalLocation loc, const PackedByteArray &rgba_data);
// ── Per-frame pipeline ──────────────────────────────────────────────
void _process_feedback();
void _process_tile_requests();
};