1 // Copyright 2020 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include "common/alignment.h"
6 #include "video_core/renderer_opengl/gl_rasterizer_cache.h"
7 #include "video_core/renderer_opengl/gl_surface_params.h"
8 
9 namespace OpenGL {
10 
FromInterval(SurfaceInterval interval) const11 SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const {
12     SurfaceParams params = *this;
13     const u32 tiled_size = is_tiled ? 8 : 1;
14     const u32 stride_tiled_bytes = BytesInPixels(stride * tiled_size);
15     PAddr aligned_start =
16         addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes);
17     PAddr aligned_end =
18         addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes);
19 
20     if (aligned_end - aligned_start > stride_tiled_bytes) {
21         params.addr = aligned_start;
22         params.height = (aligned_end - aligned_start) / BytesInPixels(stride);
23     } else {
24         // 1 row
25         ASSERT(aligned_end - aligned_start == stride_tiled_bytes);
26         const u32 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1);
27         aligned_start =
28             addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment);
29         aligned_end =
30             addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment);
31         params.addr = aligned_start;
32         params.width = PixelsInBytes(aligned_end - aligned_start) / tiled_size;
33         params.stride = params.width;
34         params.height = tiled_size;
35     }
36     params.UpdateParams();
37 
38     return params;
39 }
40 
GetSubRectInterval(Common::Rectangle<u32> unscaled_rect) const41 SurfaceInterval SurfaceParams::GetSubRectInterval(Common::Rectangle<u32> unscaled_rect) const {
42     if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) {
43         return {};
44     }
45 
46     if (is_tiled) {
47         unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8;
48         unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8;
49         unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8;
50         unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8;
51     }
52 
53     const u32 stride_tiled = !is_tiled ? stride : stride * 8;
54 
55     const u32 pixel_offset =
56         stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) +
57         unscaled_rect.left;
58 
59     const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth();
60 
61     return {addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels)};
62 }
63 
GetCopyableInterval(const Surface & src_surface) const64 SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) const {
65     SurfaceInterval result{};
66     const auto valid_regions =
67         SurfaceRegions(GetInterval() & src_surface->GetInterval()) - src_surface->invalid_regions;
68     for (auto& valid_interval : valid_regions) {
69         const SurfaceInterval aligned_interval{
70             addr + Common::AlignUp(boost::icl::first(valid_interval) - addr,
71                                    BytesInPixels(is_tiled ? 8 * 8 : 1)),
72             addr + Common::AlignDown(boost::icl::last_next(valid_interval) - addr,
73                                      BytesInPixels(is_tiled ? 8 * 8 : 1))};
74 
75         if (BytesInPixels(is_tiled ? 8 * 8 : 1) > boost::icl::length(valid_interval) ||
76             boost::icl::length(aligned_interval) == 0) {
77             continue;
78         }
79 
80         // Get the rectangle within aligned_interval
81         const u32 stride_bytes = BytesInPixels(stride) * (is_tiled ? 8 : 1);
82         SurfaceInterval rect_interval{
83             addr + Common::AlignUp(boost::icl::first(aligned_interval) - addr, stride_bytes),
84             addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - addr, stride_bytes),
85         };
86         if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
87             // 1 row
88             rect_interval = aligned_interval;
89         } else if (boost::icl::length(rect_interval) == 0) {
90             // 2 rows that do not make a rectangle, return the larger one
91             const SurfaceInterval row1{boost::icl::first(aligned_interval),
92                                        boost::icl::first(rect_interval)};
93             const SurfaceInterval row2{boost::icl::first(rect_interval),
94                                        boost::icl::last_next(aligned_interval)};
95             rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2;
96         }
97 
98         if (boost::icl::length(rect_interval) > boost::icl::length(result)) {
99             result = rect_interval;
100         }
101     }
102     return result;
103 }
104 
GetSubRect(const SurfaceParams & sub_surface) const105 Common::Rectangle<u32> SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const {
106     const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr);
107 
108     if (is_tiled) {
109         const int x0 = (begin_pixel_index % (stride * 8)) / 8;
110         const int y0 = (begin_pixel_index / (stride * 8)) * 8;
111         // Top to bottom
112         return Common::Rectangle<u32>(x0, height - y0, x0 + sub_surface.width,
113                                       height - (y0 + sub_surface.height));
114     }
115 
116     const int x0 = begin_pixel_index % stride;
117     const int y0 = begin_pixel_index / stride;
118     // Bottom to top
119     return Common::Rectangle<u32>(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0);
120 }
121 
GetScaledSubRect(const SurfaceParams & sub_surface) const122 Common::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const {
123     auto rect = GetSubRect(sub_surface);
124     rect.left = rect.left * res_scale;
125     rect.right = rect.right * res_scale;
126     rect.top = rect.top * res_scale;
127     rect.bottom = rect.bottom * res_scale;
128     return rect;
129 }
130 
ExactMatch(const SurfaceParams & other_surface) const131 bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
132     return std::tie(other_surface.addr, other_surface.width, other_surface.height,
133                     other_surface.stride, other_surface.pixel_format, other_surface.is_tiled) ==
134                std::tie(addr, width, height, stride, pixel_format, is_tiled) &&
135            pixel_format != PixelFormat::Invalid;
136 }
137 
CanSubRect(const SurfaceParams & sub_surface) const138 bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const {
139     return sub_surface.addr >= addr && sub_surface.end <= end &&
140            sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid &&
141            sub_surface.is_tiled == is_tiled &&
142            (sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
143            (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) &&
144            GetSubRect(sub_surface).right <= stride;
145 }
146 
CanExpand(const SurfaceParams & expanded_surface) const147 bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const {
148     return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format &&
149            addr <= expanded_surface.end && expanded_surface.addr <= end &&
150            is_tiled == expanded_surface.is_tiled && stride == expanded_surface.stride &&
151            (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) %
152                    BytesInPixels(stride * (is_tiled ? 8 : 1)) ==
153                0;
154 }
155 
CanTexCopy(const SurfaceParams & texcopy_params) const156 bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
157     if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr ||
158         end < texcopy_params.end) {
159         return false;
160     }
161     if (texcopy_params.width != texcopy_params.stride) {
162         const u32 tile_stride = BytesInPixels(stride * (is_tiled ? 8 : 1));
163         return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
164                texcopy_params.width % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
165                (texcopy_params.height == 1 || texcopy_params.stride == tile_stride) &&
166                ((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride;
167     }
168     return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval();
169 }
170 
171 } // namespace OpenGL
172