From 53c9611ac26449ad7782cafaa77b950d264b47ba Mon Sep 17 00:00:00 2001
From: Samuliak <samuliak77@gmail.com>
Date: Tue, 2 Jul 2024 16:06:20 +0200
Subject: [PATCH] add: render target caches

---
 include/renderer_mtl/render_target.hpp | 77 ++++++++++++++++++++++++++
 include/renderer_mtl/renderer_mtl.hpp  |  3 +
 src/core/renderer_mtl/renderer_mtl.cpp |  4 ++
 src/core/renderer_mtl/texture.cpp      |  2 +-
 4 files changed, 85 insertions(+), 1 deletion(-)
 create mode 100644 include/renderer_mtl/render_target.hpp

diff --git a/include/renderer_mtl/render_target.hpp b/include/renderer_mtl/render_target.hpp
new file mode 100644
index 00000000..26c356a6
--- /dev/null
+++ b/include/renderer_mtl/render_target.hpp
@@ -0,0 +1,77 @@
+#pragma once
+#include <array>
+#include <string>
+#include <Metal/Metal.hpp>
+#include "PICA/regs.hpp"
+#include "boost/icl/interval.hpp"
+#include "helpers.hpp"
+#include "math_util.hpp"
+#include "opengl.hpp"
+
+template <typename T>
+using Interval = boost::icl::right_open_interval<T>;
+
+namespace Metal {
+
+struct RenderTarget {
+    MTL::Device* device;
+
+    u32 location;
+    PICA::TextureFmt format;
+    OpenGL::uvec2 size;
+    bool valid;
+
+    // Range of VRAM taken up by buffer
+    Interval<u32> range;
+
+    MTL::Texture* texture = nullptr;
+
+    RenderTarget() : valid(false) {}
+
+    RenderTarget(MTL::Device* dev, u32 loc, PICA::TextureFmt format, u32 x, u32 y, bool valid = true)
+        : device(dev), location(loc), format(format), size({x, y}), valid(valid) {
+
+        u64 endLoc = (u64)loc + sizeInBytes();
+        // Check if start and end are valid here
+        range = Interval<u32>(loc, (u32)endLoc);
+    }
+
+	Math::Rect<u32> getSubRect(u32 inputAddress, u32 width, u32 height) {
+		const u32 startOffset = (inputAddress - location) / sizePerPixel(format);
+		const u32 x0 = (startOffset % (size.x() * 8)) / 8;
+		const u32 y0 = (startOffset / (size.x() * 8)) * 8;
+		return Math::Rect<u32>{x0, y0, x0 + width, y0 + height};
+	}
+
+    // For 2 textures to "match" we only care about their locations, formats, and dimensions to match
+    // For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
+    bool matches(Texture& other) {
+        return location == other.location && format == other.format &&
+            size.x() == other.size.x() && size.y() == other.size.y();
+    }
+
+    void allocate() {
+        MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
+        descriptor->setTextureType(MTL::TextureType2D);
+        descriptor->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
+        descriptor->setWidth(size.u());
+        descriptor->setHeight(size.v());
+        descriptor->setUsage(MTL::TextureUsageRenderTarget | MTL::TextureUsageShaderRead);
+        descriptor->setStorageMode(MTL::StorageModePrivate);
+        texture = device->newTexture(descriptor);
+    }
+
+    void free() {
+        valid = false;
+
+    	if (texture) {
+    		texture->release();
+    	}
+    }
+
+    u64 sizeInBytes() {
+        return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
+    }
+};
+
+} // namespace Metal
diff --git a/include/renderer_mtl/renderer_mtl.hpp b/include/renderer_mtl/renderer_mtl.hpp
index 56415bc8..4e67cdec 100644
--- a/include/renderer_mtl/renderer_mtl.hpp
+++ b/include/renderer_mtl/renderer_mtl.hpp
@@ -3,6 +3,7 @@
 
 #include "renderer.hpp"
 #include "texture.hpp"
+#include "render_target.hpp"
 // HACK: use the OpenGL cache
 #include "../renderer_gl/surface_cache.hpp"
 
@@ -34,6 +35,8 @@ class RendererMTL final : public Renderer {
 	MTL::CommandQueue* commandQueue;
 
 	// Caches
+	SurfaceCache<Metal::Texture, 16, true> colorRenderTargetCache;
+	SurfaceCache<Metal::Texture, 16, true> depthStencilRenderTargetCache;
 	SurfaceCache<Metal::Texture, 256, true> textureCache;
 
 	// HACK
diff --git a/src/core/renderer_mtl/renderer_mtl.cpp b/src/core/renderer_mtl/renderer_mtl.cpp
index 93b821c1..e0fc1c3a 100644
--- a/src/core/renderer_mtl/renderer_mtl.cpp
+++ b/src/core/renderer_mtl/renderer_mtl.cpp
@@ -18,6 +18,8 @@ RendererMTL::RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs,
 RendererMTL::~RendererMTL() {}
 
 void RendererMTL::reset() {
+    colorRenderTargetCache.reset();
+    depthStencilRenderTargetCache.reset();
     textureCache.reset();
 
 	// TODO: implement
@@ -233,6 +235,8 @@ void RendererMTL::screenshot(const std::string& name) {
 }
 
 void RendererMTL::deinitGraphicsContext() {
+    colorRenderTargetCache.reset();
+    depthStencilRenderTargetCache.reset();
     textureCache.reset();
 
 	// TODO: implement
diff --git a/src/core/renderer_mtl/texture.cpp b/src/core/renderer_mtl/texture.cpp
index df3866ac..f50fdca7 100644
--- a/src/core/renderer_mtl/texture.cpp
+++ b/src/core/renderer_mtl/texture.cpp
@@ -12,7 +12,7 @@ void Texture::allocate() {
     descriptor->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
     descriptor->setWidth(size.u());
     descriptor->setHeight(size.v());
-    descriptor->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite);
+    descriptor->setUsage(MTL::TextureUsageShaderRead);
     descriptor->setStorageMode(MTL::StorageModeShared); // TODO: use private + staging buffers?
     texture = device->newTexture(descriptor);