summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/framebuffer_layout.cpp338
-rw-r--r--src/common/math_util.h12
2 files changed, 100 insertions, 250 deletions
diff --git a/src/common/framebuffer_layout.cpp b/src/common/framebuffer_layout.cpp
index 42cfa32a9..d50c141bb 100644
--- a/src/common/framebuffer_layout.cpp
+++ b/src/common/framebuffer_layout.cpp
@@ -9,290 +9,128 @@
9#include "video_core/video_core.h" 9#include "video_core/video_core.h"
10 10
11namespace Layout { 11namespace Layout {
12static FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
13 12
14 ASSERT(width > 0); 13static const float TOP_SCREEN_ASPECT_RATIO =
15 ASSERT(height > 0); 14 static_cast<float>(VideoCore::kScreenTopHeight) / VideoCore::kScreenTopWidth;
16 15static const float BOT_SCREEN_ASPECT_RATIO =
17 FramebufferLayout res {width, height, true, true, {}, {}}; 16 static_cast<float>(VideoCore::kScreenBottomHeight) / VideoCore::kScreenBottomWidth;
18 17static const float BOT_TO_TOP_SCREEN_RATIO_DIFFERENCE =
19 float window_aspect_ratio = static_cast<float>(height) / width; 18 BOT_SCREEN_ASPECT_RATIO - TOP_SCREEN_ASPECT_RATIO;
20 float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) / 19
21 VideoCore::kScreenTopWidth; 20// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
22 21template <class T>
23 if (window_aspect_ratio > emulation_aspect_ratio) { 22static MathUtil::Rectangle<T> maxRectangle(MathUtil::Rectangle<T> window_area,
24 // Window is narrower than the emulation content => apply borders to the top and bottom 23 float screen_aspect_ratio) {
25 int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width)); 24 float scale = std::min(static_cast<float>(window_area.GetWidth()),
26 25 window_area.GetHeight() / screen_aspect_ratio);
27 res.top_screen.left = 0; 26 return MathUtil::Rectangle<T>{0, 0, static_cast<T>(scale),
28 res.top_screen.right = res.top_screen.left + width; 27 static_cast<T>(scale * screen_aspect_ratio)};
29 res.top_screen.top = (height - viewport_height) / 2;
30 res.top_screen.bottom = res.top_screen.top + viewport_height / 2;
31
32 int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
33 VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
34 int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
35
36 res.bottom_screen.left = bottom_border;
37 res.bottom_screen.right = res.bottom_screen.left + bottom_width;
38 res.bottom_screen.top = res.top_screen.bottom;
39 res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2;
40 } else {
41 // Otherwise, apply borders to the left and right sides of the window.
42 int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio));
43
44 res.top_screen.left = (width - viewport_width) / 2;
45 res.top_screen.right = res.top_screen.left + viewport_width;
46 res.top_screen.top = 0;
47 res.top_screen.bottom = res.top_screen.top + height / 2;
48
49 int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
50 VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
51 int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
52
53 res.bottom_screen.left = res.top_screen.left + bottom_border;
54 res.bottom_screen.right = res.bottom_screen.left + bottom_width;
55 res.bottom_screen.top = res.top_screen.bottom;
56 res.bottom_screen.bottom = res.bottom_screen.top + height / 2;
57 }
58
59 return res;
60} 28}
61 29
62static FramebufferLayout DefaultFrameLayout_Swapped(unsigned width, unsigned height) { 30FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height, bool swapped) {
63
64 ASSERT(width > 0); 31 ASSERT(width > 0);
65 ASSERT(height > 0); 32 ASSERT(height > 0);
66 33
67 FramebufferLayout res {width, height, true, true, {}, {}}; 34 FramebufferLayout res{width, height, true, true, {}, {}};
35 // Default layout gives equal screen sizes to the top and bottom screen
36 MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height / 2};
37 MathUtil::Rectangle<unsigned> top_screen =
38 maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
39 MathUtil::Rectangle<unsigned> bot_screen =
40 maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
68 41
69 float window_aspect_ratio = static_cast<float>(height) / width; 42 float window_aspect_ratio = static_cast<float>(height) / width;
70 float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) / 43 // both screens height are taken into account by multiplying by 2
71 VideoCore::kScreenTopWidth; 44 float emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2;
72
73 if (window_aspect_ratio > emulation_aspect_ratio) {
74 // Window is narrower than the emulation content => apply borders to the top and bottom
75 int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width));
76
77 res.top_screen.left = 0;
78 res.top_screen.right = res.top_screen.left + width;
79 45
80 int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) / 46 if (window_aspect_ratio < emulation_aspect_ratio) {
81 VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left)); 47 // Apply borders to the left and right sides of the window.
82 int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; 48 top_screen =
83 49 top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
84 res.bottom_screen.left = bottom_border; 50 bot_screen =
85 res.bottom_screen.right = res.bottom_screen.left + bottom_width; 51 bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
86 res.bottom_screen.top = (height - viewport_height) / 2;
87 res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2;
88
89 res.top_screen.top = res.bottom_screen.bottom;
90 res.top_screen.bottom = res.top_screen.top + viewport_height / 2;
91 } else { 52 } else {
92 // Otherwise, apply borders to the left and right sides of the window.
93 int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio));
94 res.top_screen.left = (width - viewport_width) / 2;
95 res.top_screen.right = res.top_screen.left + viewport_width;
96
97 int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
98 VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
99 int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
100
101 res.bottom_screen.left = res.top_screen.left + bottom_border;
102 res.bottom_screen.right = res.bottom_screen.left + bottom_width;
103 res.bottom_screen.top = 0;
104 res.bottom_screen.bottom = res.bottom_screen.top + height / 2;
105
106 res.top_screen.top = res.bottom_screen.bottom;
107 res.top_screen.bottom = res.top_screen.top + height / 2;
108 }
109
110 return res;
111}
112
113static FramebufferLayout SingleFrameLayout(unsigned width, unsigned height) {
114
115 ASSERT(width > 0);
116 ASSERT(height > 0);
117
118 FramebufferLayout res {width, height, true, false, {}, {}};
119
120 float window_aspect_ratio = static_cast<float>(height) / width;
121 float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight) /
122 VideoCore::kScreenTopWidth;
123
124 if (window_aspect_ratio > emulation_aspect_ratio) {
125 // Window is narrower than the emulation content => apply borders to the top and bottom 53 // Window is narrower than the emulation content => apply borders to the top and bottom
126 int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width)); 54 top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight());
127 55 // Recalculate the bottom screen to account for the width difference between top and bottom
128 res.top_screen.left = 0; 56 screen_window_area = {0, 0, width, top_screen.GetHeight()};
129 res.top_screen.right = res.top_screen.left + width; 57 bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
130 res.top_screen.top = (height - viewport_height) / 2; 58 bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2);
131 res.top_screen.bottom = res.top_screen.top + viewport_height;
132
133 res.bottom_screen.left = 0;
134 res.bottom_screen.right = VideoCore::kScreenBottomWidth;
135 res.bottom_screen.top = 0;
136 res.bottom_screen.bottom = VideoCore::kScreenBottomHeight;
137 } else {
138 // Otherwise, apply borders to the left and right sides of the window.
139 int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio));
140
141 res.top_screen.left = (width - viewport_width) / 2;
142 res.top_screen.right = res.top_screen.left + viewport_width;
143 res.top_screen.top = 0;
144 res.top_screen.bottom = res.top_screen.top + height;
145
146 // The Rasterizer still depends on these fields to maintain the right aspect ratio
147 res.bottom_screen.left = 0;
148 res.bottom_screen.right = VideoCore::kScreenBottomWidth;
149 res.bottom_screen.top = 0;
150 res.bottom_screen.bottom = VideoCore::kScreenBottomHeight;
151 } 59 }
152 60 // Move the top screen to the bottom if we are swapped.
61 res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen;
62 res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2);
153 return res; 63 return res;
154} 64}
155 65
156static FramebufferLayout SingleFrameLayout_Swapped(unsigned width, unsigned height) { 66FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool swapped) {
157
158 ASSERT(width > 0); 67 ASSERT(width > 0);
159 ASSERT(height > 0); 68 ASSERT(height > 0);
69 // The drawing code needs at least somewhat valid values for both screens
70 // so just calculate them both even if the other isn't showing.
71 FramebufferLayout res{width, height, !swapped, swapped, {}, {}};
160 72
161 FramebufferLayout res {width, height, false, true, {}, {}}; 73 MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height};
74 MathUtil::Rectangle<unsigned> top_screen =
75 maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
76 MathUtil::Rectangle<unsigned> bot_screen =
77 maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
162 78
163 float window_aspect_ratio = static_cast<float>(height) / width; 79 float window_aspect_ratio = static_cast<float>(height) / width;
164 float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenBottomHeight) / 80 float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
165 VideoCore::kScreenBottomWidth;
166
167 if (window_aspect_ratio > emulation_aspect_ratio) {
168 // Window is narrower than the emulation content => apply borders to the top and bottom
169 int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width));
170
171 res.bottom_screen.left = 0;
172 res.bottom_screen.right = res.bottom_screen.left + width;
173 res.bottom_screen.top = (height - viewport_height) / 2;
174 res.bottom_screen.bottom = res.bottom_screen.top + viewport_height;
175
176 // The Rasterizer still depends on these fields to maintain the right aspect ratio
177 res.top_screen.left = 0;
178 res.top_screen.right = VideoCore::kScreenTopWidth;
179 res.top_screen.top = 0;
180 res.top_screen.bottom = VideoCore::kScreenTopHeight;
181 } else {
182 // Otherwise, apply borders to the left and right sides of the window.
183 int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio));
184
185 res.bottom_screen.left = (width - viewport_width) / 2;
186 res.bottom_screen.right = res.bottom_screen.left + viewport_width;
187 res.bottom_screen.top = 0;
188 res.bottom_screen.bottom = res.bottom_screen.top + height;
189
190 res.top_screen.left = 0;
191 res.top_screen.right = VideoCore::kScreenTopWidth;
192 res.top_screen.top = 0;
193 res.top_screen.bottom = VideoCore::kScreenTopHeight;
194 }
195
196 return res;
197}
198
199static FramebufferLayout LargeFrameLayout(unsigned width, unsigned height) {
200
201 ASSERT(width > 0);
202 ASSERT(height > 0);
203
204 FramebufferLayout res{ width, height, true, true,{},{} };
205
206 float window_aspect_ratio = static_cast<float>(width) / height;
207 float top_screen_aspect_ratio = static_cast<float>(VideoCore::kScreenTopWidth) /
208 VideoCore::kScreenTopHeight;
209
210 int viewport_height = static_cast<int>(std::round((width - VideoCore::kScreenBottomWidth) /
211 top_screen_aspect_ratio));
212 int viewport_width = static_cast<int>(std::round((height * top_screen_aspect_ratio) +
213 VideoCore::kScreenBottomWidth));
214 float emulation_aspect_ratio = static_cast<float>(width) / viewport_height;
215 81
216 if (window_aspect_ratio < emulation_aspect_ratio) { 82 if (window_aspect_ratio < emulation_aspect_ratio) {
217 // Window is narrower than the emulation content => apply borders to the top and bottom 83 top_screen =
218 res.top_screen.left = 0; 84 top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
219 res.top_screen.right = width - VideoCore::kScreenBottomWidth; 85 bot_screen =
220 res.top_screen.top = (height - viewport_height) / 2; 86 bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
221 res.top_screen.bottom = viewport_height + res.top_screen.top;
222
223 res.bottom_screen.left = res.top_screen.right;
224 res.bottom_screen.right = width;
225 res.bottom_screen.bottom = res.top_screen.bottom;
226 res.bottom_screen.top = res.bottom_screen.bottom - VideoCore::kScreenBottomHeight;
227 } else { 87 } else {
228 // Otherwise, apply borders to the left and right sides of the window. 88 top_screen = top_screen.TranslateY((height - top_screen.GetHeight()) / 2);
229 res.top_screen.left = (width - viewport_width) / 2; 89 bot_screen = bot_screen.TranslateY((height - bot_screen.GetHeight()) / 2);
230 res.top_screen.right = (top_screen_aspect_ratio * height) + res.top_screen.left;
231 res.top_screen.top = 0;
232 res.top_screen.bottom = height;
233
234 res.bottom_screen.left = res.top_screen.right;
235 res.bottom_screen.right = res.bottom_screen.left + VideoCore::kScreenBottomWidth;
236 res.bottom_screen.bottom = height;
237 res.bottom_screen.top = height - VideoCore::kScreenBottomHeight;
238 } 90 }
239 91 res.top_screen = top_screen;
92 res.bottom_screen = bot_screen;
240 return res; 93 return res;
241} 94}
242 95
243static FramebufferLayout LargeFrameLayout_Swapped(unsigned width, unsigned height) { 96FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped) {
244
245 ASSERT(width > 0); 97 ASSERT(width > 0);
246 ASSERT(height > 0); 98 ASSERT(height > 0);
247 99
248 FramebufferLayout res {width, height, true, true, {}, {}}; 100 FramebufferLayout res{width, height, true, true, {}, {}};
249 101 // Split the window into two parts. Give 4x width to the main screen and 1x width to the small
250 float window_aspect_ratio = static_cast<float>(width) / height; 102 // To do that, find the total emulation box and maximize that based on window size
251 float bottom_screen_aspect_ratio = static_cast<float>(VideoCore::kScreenBottomWidth) / 103 float window_aspect_ratio = static_cast<float>(height) / width;
252 VideoCore::kScreenBottomHeight; 104 float emulation_aspect_ratio =
253 105 swapped
254 int viewport_height = static_cast<int>(std::round((width - VideoCore::kScreenTopWidth) / 106 ? VideoCore::kScreenBottomHeight * 4 /
255 bottom_screen_aspect_ratio)); 107 (VideoCore::kScreenBottomWidth * 4.0f + VideoCore::kScreenTopWidth)
256 int viewport_width = static_cast<int>(std::round((height * bottom_screen_aspect_ratio) + 108 : VideoCore::kScreenTopHeight * 4 /
257 VideoCore::kScreenTopWidth)); 109 (VideoCore::kScreenTopWidth * 4.0f + VideoCore::kScreenBottomWidth);
258 float emulation_aspect_ratio = static_cast<float>(width) / viewport_height; 110 float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
111 float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO;
112
113 MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height};
114 MathUtil::Rectangle<unsigned> total_rect =
115 maxRectangle(screen_window_area, emulation_aspect_ratio);
116 MathUtil::Rectangle<unsigned> large_screen =
117 maxRectangle(total_rect, large_screen_aspect_ratio);
118 MathUtil::Rectangle<unsigned> fourth_size_rect = total_rect.Scale(.25f);
119 MathUtil::Rectangle<unsigned> small_screen =
120 maxRectangle(fourth_size_rect, small_screen_aspect_ratio);
259 121
260 if (window_aspect_ratio < emulation_aspect_ratio) { 122 if (window_aspect_ratio < emulation_aspect_ratio) {
261 // Window is narrower than the emulation content => apply borders to the top and bottom 123 large_screen =
262 res.bottom_screen.left = 0; 124 large_screen.TranslateX((screen_window_area.GetWidth() - total_rect.GetWidth()) / 2);
263 res.bottom_screen.right = width - VideoCore::kScreenTopWidth;
264 res.bottom_screen.top = (height - viewport_height) / 2;
265 res.bottom_screen.bottom = viewport_height + res.bottom_screen.top;
266
267 res.top_screen.left = res.bottom_screen.right;
268 res.top_screen.right = width;
269 res.top_screen.bottom = res.bottom_screen.bottom;
270 res.top_screen.top = res.top_screen.bottom - VideoCore::kScreenTopHeight;
271 } else { 125 } else {
272 // Otherwise, apply borders to the left and right sides of the window. 126 large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2);
273 res.bottom_screen.left = (width - viewport_width) / 2;
274 res.bottom_screen.right = (bottom_screen_aspect_ratio * height) + res.bottom_screen.left;
275 res.bottom_screen.top = 0;
276 res.bottom_screen.bottom = height;
277
278 res.top_screen.left = res.bottom_screen.right;
279 res.top_screen.right = res.top_screen.left + VideoCore::kScreenTopWidth;
280 res.top_screen.bottom = height;
281 res.top_screen.top = height - VideoCore::kScreenTopHeight;
282 } 127 }
283 128 // Shift the small screen to the bottom right corner
129 small_screen =
130 small_screen.TranslateX(large_screen.right)
131 .TranslateY(large_screen.GetHeight() + large_screen.top - small_screen.GetHeight());
132 res.top_screen = swapped ? small_screen : large_screen;
133 res.bottom_screen = swapped ? large_screen : small_screen;
284 return res; 134 return res;
285} 135}
286
287FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height, bool is_swapped) {
288 return is_swapped ? DefaultFrameLayout_Swapped(width, height) : DefaultFrameLayout(width, height);
289}
290
291FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swapped) {
292 return is_swapped ? SingleFrameLayout_Swapped(width, height) : SingleFrameLayout(width, height);
293}
294
295FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped) {
296 return is_swapped ? LargeFrameLayout_Swapped(width, height) : LargeFrameLayout(width, height);
297}
298} 136}
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 41d89666c..570ec8e56 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -38,6 +38,18 @@ struct Rectangle {
38 T GetHeight() const { 38 T GetHeight() const {
39 return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); 39 return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top));
40 } 40 }
41 Rectangle<T> TranslateX(const T x) const {
42 return Rectangle{left + x, top, right + x, bottom};
43 }
44 Rectangle<T> TranslateY(const T y) const {
45 return Rectangle{left, top + y, right, bottom + y};
46 }
47 Rectangle<T> Scale(const float s) const {
48 ASSERT(s > 0);
49 return Rectangle {
50 left, top, static_cast<T>((right + left) * s), static_cast<T>((top + bottom) * s)
51 };
52 }
41}; 53};
42 54
43} // namespace MathUtil 55} // namespace MathUtil