Add water refraction toggle with per-frame scene history

Fix VK_ERROR_DEVICE_LOST crash by allocating per-frame scene history
images (color + depth) instead of a single shared image that raced
between frames in flight. Water refraction can now be toggled via
Settings > Video > Water Refraction.

Without refraction: richer blue base colors, animated caustic shimmer,
and normal-based color shifts give the water visible life. With
refraction: clean screen-space refraction with Beer-Lambert absorption.
Disabling clears scene history to black for immediate fallback.
This commit is contained in:
Kelsi 2026-03-06 19:15:34 -08:00
parent 7630c7aec7
commit 5a227c0376
8 changed files with 323 additions and 191 deletions

View file

@ -256,6 +256,9 @@ public:
bool areShadowsEnabled() const { return shadowsEnabled; }
void setMsaaSamples(VkSampleCountFlagBits samples);
void setWaterRefractionEnabled(bool enabled);
bool isWaterRefractionEnabled() const;
private:
void applyMsaaChange();
VkSampleCountFlagBits pendingMsaaSamples_ = VK_SAMPLE_COUNT_1_BIT;

View file

@ -93,12 +93,13 @@ public:
bool hasWater1xPass() const { return water1xRenderPass != VK_NULL_HANDLE; }
VkRenderPass getWater1xRenderPass() const { return water1xRenderPass; }
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera, float time, bool use1x = false);
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera, float time, bool use1x = false, uint32_t frameIndex = 0);
void captureSceneHistory(VkCommandBuffer cmd,
VkImage srcColorImage,
VkImage srcDepthImage,
VkExtent2D srcExtent,
bool srcDepthIsMsaa);
bool srcDepthIsMsaa,
uint32_t frameIndex = 0);
// --- Planar reflection pass ---
// Call sequence: beginReflectionPass → [render scene] → endReflectionPass
@ -124,6 +125,9 @@ public:
void setEnabled(bool enabled) { renderingEnabled = enabled; }
bool isEnabled() const { return renderingEnabled; }
void setRefractionEnabled(bool enabled);
bool isRefractionEnabled() const { return refractionEnabled; }
std::optional<float> getWaterHeightAt(float glX, float glY) const;
/// Like getWaterHeightAt but only returns water surfaces whose height is
/// close to the query Z (within maxAbove units above). Avoids false
@ -159,17 +163,22 @@ private:
VkDescriptorPool materialDescPool = VK_NULL_HANDLE;
VkDescriptorSetLayout sceneSetLayout = VK_NULL_HANDLE;
VkDescriptorPool sceneDescPool = VK_NULL_HANDLE;
VkDescriptorSet sceneSet = VK_NULL_HANDLE;
static constexpr uint32_t MAX_WATER_SETS = 16384;
VkSampler sceneColorSampler = VK_NULL_HANDLE;
VkSampler sceneDepthSampler = VK_NULL_HANDLE;
VkImage sceneColorImage = VK_NULL_HANDLE;
VmaAllocation sceneColorAlloc = VK_NULL_HANDLE;
VkImageView sceneColorView = VK_NULL_HANDLE;
VkImage sceneDepthImage = VK_NULL_HANDLE;
VmaAllocation sceneDepthAlloc = VK_NULL_HANDLE;
VkImageView sceneDepthView = VK_NULL_HANDLE;
// Per-frame scene history to avoid race between frames in flight
static constexpr uint32_t SCENE_HISTORY_FRAMES = 2;
struct PerFrameSceneHistory {
VkImage colorImage = VK_NULL_HANDLE;
VmaAllocation colorAlloc = VK_NULL_HANDLE;
VkImageView colorView = VK_NULL_HANDLE;
VkImage depthImage = VK_NULL_HANDLE;
VmaAllocation depthAlloc = VK_NULL_HANDLE;
VkImageView depthView = VK_NULL_HANDLE;
VkDescriptorSet sceneSet = VK_NULL_HANDLE;
};
PerFrameSceneHistory sceneHistory[SCENE_HISTORY_FRAMES];
VkExtent2D sceneHistoryExtent = {0, 0};
bool sceneHistoryReady = false;
mutable uint32_t renderDiagCounter_ = 0;
@ -200,6 +209,7 @@ private:
std::vector<WaterSurface> surfaces;
bool renderingEnabled = true;
bool refractionEnabled = false;
};
} // namespace rendering

View file

@ -87,6 +87,7 @@ private:
bool pendingVsync = false;
int pendingResIndex = 0;
bool pendingShadows = true;
bool pendingWaterRefraction = false;
int pendingMasterVolume = 100;
int pendingMusicVolume = 30;
int pendingAmbientVolume = 100;
@ -123,6 +124,7 @@ private:
bool minimapSettingsApplied_ = false;
bool volumeSettingsApplied_ = false; // True once saved volume settings applied to audio managers
bool msaaSettingsApplied_ = false; // True once saved MSAA setting applied to renderer
bool waterRefractionApplied_ = false;
bool normalMapSettingsApplied_ = false; // True once saved normal map/POM settings applied
// Mute state: mute bypasses master volume without touching slider values