From a7b3b1e3a49718d9a3dec166b231f299e12083b2 Mon Sep 17 00:00:00 2001
From: WinterSnowfall <WinterSnowfall@users.noreply.github.com>
Date: Sat, 13 Jul 2024 20:17:09 +0300
Subject: [PATCH] [d3d8] Add an option to respect DISCARD only for dynamic
 write-only buffers

---
 src/d3d8/d3d8_buffer.h  | 13 +++++++++++--
 src/d3d8/d3d8_device.h  |  4 ++++
 src/d3d8/d3d8_options.h |  8 ++++++++
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/d3d8/d3d8_buffer.h b/src/d3d8/d3d8_buffer.h
index 0823277e7be..be5ca8451ab 100644
--- a/src/d3d8/d3d8_buffer.h
+++ b/src/d3d8/d3d8_buffer.h
@@ -18,6 +18,7 @@ namespace dxvk {
       : D3D8Resource<D3D9, D3D8> (pDevice, std::move(pBuffer))
       , m_pool                   (Pool)
       , m_usage                  (Usage) {
+      m_options = this->GetParent()->GetOptions();
     }
 
     HRESULT STDMETHODCALLTYPE Lock(
@@ -25,6 +26,13 @@ namespace dxvk {
             UINT   SizeToLock,
             BYTE** ppbData,
             DWORD  Flags) {
+
+      if (m_options->forceLegacyDiscard &&
+          (Flags & D3DLOCK_DISCARD) &&
+         !((m_usage & D3DUSAGE_DYNAMIC) &&
+           (m_usage & D3DUSAGE_WRITEONLY)))
+          Flags &= ~D3DLOCK_DISCARD;
+
       return this->GetD3D9()->Lock(
         OffsetToLock,
         SizeToLock,
@@ -41,8 +49,9 @@ namespace dxvk {
     }
 
   protected:
-    const D3DPOOL m_pool;
-    const DWORD   m_usage;
+    const D3D8Options* m_options;
+    const D3DPOOL      m_pool;
+    const DWORD        m_usage;
   };
 
 
diff --git a/src/d3d8/d3d8_device.h b/src/d3d8/d3d8_device.h
index 878e844093a..51c3fa2dd4a 100644
--- a/src/d3d8/d3d8_device.h
+++ b/src/d3d8/d3d8_device.h
@@ -358,6 +358,10 @@ namespace dxvk {
 
   public: // Internal Methods //
 
+    const D3D8Options* GetOptions() const {
+      return &m_d3d8Options;
+    }
+
     inline bool ShouldRecord() { return m_recorder != nullptr; }
     inline bool ShouldBatch()  { return m_batcher  != nullptr; }
 
diff --git a/src/d3d8/d3d8_options.h b/src/d3d8/d3d8_options.h
index 67bdf35927c..2b5047617c4 100644
--- a/src/d3d8/d3d8_options.h
+++ b/src/d3d8/d3d8_options.h
@@ -34,11 +34,19 @@ namespace dxvk {
     /// and above. Most likely ATI/AMD drivers never supported P8 in the first place.
     bool placeP8InScratch = false;
 
+    /// Rayman 3 relies on D3DLOCK_DISCARD being ignored for everything except D3DUSAGE_DYNAMIC +
+    /// D3DUSAGE_WRITEONLY buffers, however this approach incurs a performance penalty.
+    ///
+    /// Some titles might abuse this early D3D8 quirk, however at some point in its history
+    /// it was brought in line with standard D3D9 behavior.
+    bool forceLegacyDiscard = false;
+
     D3D8Options() {}
     D3D8Options(const Config& config) {
       auto forceVsDeclStr     = config.getOption<std::string>("d3d8.forceVsDecl",            "");
       batching                = config.getOption<bool>       ("d3d8.batching",               batching);
       placeP8InScratch        = config.getOption<bool>       ("d3d8.placeP8InScratch",       placeP8InScratch);
+      forceLegacyDiscard      = config.getOption<bool>       ("d3d8.forceLegacyDiscard",     forceLegacyDiscard);
 
       parseVsDecl(forceVsDeclStr);
     }