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, ¶ms, 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