/ src / video_core / rasterizer_cache / framebuffer_base.h
framebuffer_base.h
  1  // Copyright 2023 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #pragma once
  6  
  7  #include "common/hash.h"
  8  #include "common/math_util.h"
  9  #include "video_core/pica/regs_rasterizer.h"
 10  #include "video_core/rasterizer_cache/slot_id.h"
 11  #include "video_core/rasterizer_cache/surface_params.h"
 12  
 13  namespace VideoCore {
 14  
 15  class SurfaceBase;
 16  
 17  struct ViewportInfo {
 18      s32 x;
 19      s32 y;
 20      s32 width;
 21      s32 height;
 22  };
 23  
 24  struct FramebufferParams {
 25      SurfaceId color_id;
 26      SurfaceId depth_id;
 27      u32 color_level;
 28      u32 depth_level;
 29      bool shadow_rendering;
 30      INSERT_PADDING_BYTES(3);
 31  
 32      bool operator==(const FramebufferParams& params) const noexcept {
 33          return std::memcmp(this, &params, sizeof(FramebufferParams)) == 0;
 34      }
 35  
 36      u64 Hash() const noexcept {
 37          return Common::ComputeHash64(this, sizeof(FramebufferParams));
 38      }
 39  
 40      u32 Index(VideoCore::SurfaceType type) const noexcept {
 41          switch (type) {
 42          case VideoCore::SurfaceType::Color:
 43              return 0;
 44          case VideoCore::SurfaceType::Depth:
 45          case VideoCore::SurfaceType::DepthStencil:
 46              return 1;
 47          default:
 48              LOG_CRITICAL(HW_GPU, "Unknown surface type in framebuffer");
 49              return 0;
 50          }
 51      }
 52  };
 53  static_assert(std::has_unique_object_representations_v<FramebufferParams>,
 54                "FramebufferParams is not suitable for hashing");
 55  
 56  template <class T>
 57  class RasterizerCache;
 58  
 59  /**
 60   * @brief FramebufferHelper is a RAII wrapper over backend specific framebuffer handle that
 61   * provides the viewport/scissor/draw rectanges and performs automatic rasterizer cache invalidation
 62   * when out of scope.
 63   */
 64  template <class T>
 65  class FramebufferHelper {
 66  public:
 67      explicit FramebufferHelper(RasterizerCache<T>* res_cache_, typename T::Framebuffer* fb_,
 68                                 const Pica::RasterizerRegs& regs,
 69                                 Common::Rectangle<u32> surfaces_rect)
 70          : res_cache{res_cache_}, fb{fb_} {
 71          const u32 res_scale = fb->Scale();
 72  
 73          // Determine the draw rectangle (render area + scissor)
 74          const Common::Rectangle viewport_rect = regs.GetViewportRect();
 75          draw_rect.left =
 76              std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale,
 77                              surfaces_rect.left, surfaces_rect.right);
 78          draw_rect.top =
 79              std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top * res_scale,
 80                              surfaces_rect.bottom, surfaces_rect.top);
 81          draw_rect.right =
 82              std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right * res_scale,
 83                              surfaces_rect.left, surfaces_rect.right);
 84          draw_rect.bottom = std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
 85                                                 viewport_rect.bottom * res_scale,
 86                                             surfaces_rect.bottom, surfaces_rect.top);
 87  
 88          // Update viewport
 89          viewport.x = static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale;
 90          viewport.y = static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale;
 91          viewport.width = static_cast<s32>(viewport_rect.GetWidth() * res_scale);
 92          viewport.height = static_cast<s32>(viewport_rect.GetHeight() * res_scale);
 93  
 94          // Scissor checks are window-, not viewport-relative, which means that if the cached texture
 95          // sub-rect changes, the scissor bounds also need to be updated.
 96          scissor_rect.left = static_cast<s32>(surfaces_rect.left + regs.scissor_test.x1 * res_scale);
 97          scissor_rect.bottom =
 98              static_cast<s32>(surfaces_rect.bottom + regs.scissor_test.y1 * res_scale);
 99  
100          // x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
101          // scaling or doing multisampling.
102          scissor_rect.right =
103              static_cast<s32>(surfaces_rect.left + (regs.scissor_test.x2 + 1) * res_scale);
104          scissor_rect.top =
105              static_cast<s32>(surfaces_rect.bottom + (regs.scissor_test.y2 + 1) * res_scale);
106      }
107  
108      ~FramebufferHelper() {
109          const Common::Rectangle draw_rect_unscaled{draw_rect / fb->Scale()};
110          const auto invalidate = [&](SurfaceId surface_id, u32 level) {
111              const auto& surface = res_cache->GetSurface(surface_id);
112              const SurfaceInterval interval = surface.GetSubRectInterval(draw_rect_unscaled, level);
113              const PAddr addr = boost::icl::first(interval);
114              const u32 size = boost::icl::length(interval);
115              res_cache->InvalidateRegion(addr, size, surface_id);
116          };
117          if (fb->color_id) {
118              invalidate(fb->color_id, fb->color_level);
119          }
120          if (fb->depth_id) {
121              invalidate(fb->depth_id, fb->depth_level);
122          }
123      }
124  
125      typename T::Framebuffer* Framebuffer() const noexcept {
126          return fb;
127      }
128  
129      Common::Rectangle<u32> DrawRect() const noexcept {
130          return draw_rect;
131      }
132  
133      Common::Rectangle<s32> Scissor() const noexcept {
134          return scissor_rect;
135      }
136  
137      ViewportInfo Viewport() const noexcept {
138          return viewport;
139      }
140  
141  private:
142      RasterizerCache<T>* res_cache;
143      typename T::Framebuffer* fb;
144      Common::Rectangle<s32> scissor_rect;
145      Common::Rectangle<u32> draw_rect;
146      ViewportInfo viewport;
147  };
148  
149  } // namespace VideoCore
150  
151  namespace std {
152  template <>
153  struct hash<VideoCore::FramebufferParams> {
154      std::size_t operator()(const VideoCore::FramebufferParams& params) const noexcept {
155          return params.Hash();
156      }
157  };
158  } // namespace std