summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp1362
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h350
3 files changed, 1714 insertions, 0 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 7b26fe180..92951a7e1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -13,6 +13,8 @@ add_library(video_core STATIC
13 memory_manager.h 13 memory_manager.h
14 renderer_base.cpp 14 renderer_base.cpp
15 renderer_base.h 15 renderer_base.h
16 renderer_opengl/gl_rasterizer_cache.cpp
17 renderer_opengl/gl_rasterizer_cache.h
16 renderer_opengl/gl_resource_manager.h 18 renderer_opengl/gl_resource_manager.h
17 renderer_opengl/gl_shader_util.cpp 19 renderer_opengl/gl_shader_util.cpp
18 renderer_opengl/gl_shader_util.h 20 renderer_opengl/gl_shader_util.h
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
new file mode 100644
index 000000000..e481139af
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -0,0 +1,1362 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <atomic>
7#include <cstring>
8#include <iterator>
9#include <memory>
10#include <unordered_set>
11#include <utility>
12#include <vector>
13#include <boost/optional.hpp>
14#include <boost/range/iterator_range.hpp>
15#include <glad/glad.h>
16#include "common/alignment.h"
17#include "common/bit_field.h"
18#include "common/color.h"
19#include "common/logging/log.h"
20#include "common/math_util.h"
21#include "common/microprofile.h"
22#include "common/scope_exit.h"
23#include "common/vector_math.h"
24#include "core/frontend/emu_window.h"
25#include "core/memory.h"
26#include "core/settings.h"
27#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
28#include "video_core/renderer_opengl/gl_state.h"
29#include "video_core/texture/texture_decode.h"
30#include "video_core/utils.h"
31#include "video_core/video_core.h"
32
33using SurfaceType = SurfaceParams::SurfaceType;
34using PixelFormat = SurfaceParams::PixelFormat;
35
36struct FormatTuple {
37 GLint internal_format;
38 GLenum format;
39 GLenum type;
40};
41
42static constexpr std::array<FormatTuple, 5> fb_format_tuples = {{
43 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8
44 {GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8
45 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1
46 {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
47 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4
48}};
49
50static constexpr std::array<FormatTuple, 4> depth_format_tuples = {{
51 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
52 {},
53 {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
54 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
55}};
56
57static constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
58
59static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
60 const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
61 if (type == SurfaceType::Color) {
62 ASSERT(static_cast<size_t>(pixel_format) < fb_format_tuples.size());
63 return fb_format_tuples[static_cast<unsigned int>(pixel_format)];
64 } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
65 size_t tuple_idx = static_cast<size_t>(pixel_format) - 14;
66 ASSERT(tuple_idx < depth_format_tuples.size());
67 return depth_format_tuples[tuple_idx];
68 }
69 return tex_tuple;
70}
71
72template <typename Map, typename Interval>
73constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
74 return boost::make_iterator_range(map.equal_range(interval));
75}
76
77static u16 GetResolutionScaleFactor() {
78 return static_cast<u16>(!Settings::values.resolution_factor
79 ? VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio()
80 : Settings::values.resolution_factor);
81}
82
83template <bool morton_to_gl, PixelFormat format>
84static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
85 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
86 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
87 for (u32 y = 0; y < 8; ++y) {
88 for (u32 x = 0; x < 8; ++x) {
89 u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
90 u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel;
91 if (morton_to_gl) {
92 if (format == PixelFormat::D24S8) {
93 gl_ptr[0] = tile_ptr[3];
94 std::memcpy(gl_ptr + 1, tile_ptr, 3);
95 } else {
96 std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
97 }
98 } else {
99 if (format == PixelFormat::D24S8) {
100 std::memcpy(tile_ptr, gl_ptr + 1, 3);
101 tile_ptr[3] = gl_ptr[0];
102 } else {
103 std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
104 }
105 }
106 }
107 }
108}
109
110template <bool morton_to_gl, PixelFormat format>
111static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, PAddr base, PAddr start, PAddr end) {
112 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
113 constexpr u32 tile_size = bytes_per_pixel * 64;
114
115 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
116 static_assert(gl_bytes_per_pixel >= bytes_per_pixel, "");
117 gl_buffer += gl_bytes_per_pixel - bytes_per_pixel;
118
119 const PAddr aligned_down_start = base + Common::AlignDown(start - base, tile_size);
120 const PAddr aligned_start = base + Common::AlignUp(start - base, tile_size);
121 const PAddr aligned_end = base + Common::AlignDown(end - base, tile_size);
122
123 ASSERT(!morton_to_gl || (aligned_start == start && aligned_end == end));
124
125 const u64 begin_pixel_index = (aligned_down_start - base) / bytes_per_pixel;
126 u32 x = static_cast<u32>((begin_pixel_index % (stride * 8)) / 8);
127 u32 y = static_cast<u32>((begin_pixel_index / (stride * 8)) * 8);
128
129 gl_buffer += ((height - 8 - y) * stride + x) * gl_bytes_per_pixel;
130
131 auto glbuf_next_tile = [&] {
132 x = (x + 8) % stride;
133 gl_buffer += 8 * gl_bytes_per_pixel;
134 if (!x) {
135 y += 8;
136 gl_buffer -= stride * 9 * gl_bytes_per_pixel;
137 }
138 };
139
140 u8* tile_buffer = Memory::GetPhysicalPointer(start);
141
142 if (start < aligned_start && !morton_to_gl) {
143 std::array<u8, tile_size> tmp_buf;
144 MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
145 std::memcpy(tile_buffer, &tmp_buf[start - aligned_down_start],
146 std::min(aligned_start, end) - start);
147
148 tile_buffer += aligned_start - start;
149 glbuf_next_tile();
150 }
151
152 const u8* const buffer_end = tile_buffer + aligned_end - aligned_start;
153 while (tile_buffer < buffer_end) {
154 MortonCopyTile<morton_to_gl, format>(stride, tile_buffer, gl_buffer);
155 tile_buffer += tile_size;
156 glbuf_next_tile();
157 }
158
159 if (end > std::max(aligned_start, aligned_end) && !morton_to_gl) {
160 std::array<u8, tile_size> tmp_buf;
161 MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
162 std::memcpy(tile_buffer, &tmp_buf[0], end - aligned_end);
163 }
164}
165
166static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> morton_to_gl_fns = {
167 MortonCopy<true, PixelFormat::RGBA8>, // 0
168 MortonCopy<true, PixelFormat::RGB8>, // 1
169 MortonCopy<true, PixelFormat::RGB5A1>, // 2
170 MortonCopy<true, PixelFormat::RGB565>, // 3
171 MortonCopy<true, PixelFormat::RGBA4>, // 4
172 nullptr,
173 nullptr,
174 nullptr,
175 nullptr,
176 nullptr,
177 nullptr,
178 nullptr,
179 nullptr,
180 nullptr, // 5 - 13
181 MortonCopy<true, PixelFormat::D16>, // 14
182 nullptr, // 15
183 MortonCopy<true, PixelFormat::D24>, // 16
184 MortonCopy<true, PixelFormat::D24S8> // 17
185};
186
187static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gl_to_morton_fns = {
188 MortonCopy<false, PixelFormat::RGBA8>, // 0
189 MortonCopy<false, PixelFormat::RGB8>, // 1
190 MortonCopy<false, PixelFormat::RGB5A1>, // 2
191 MortonCopy<false, PixelFormat::RGB565>, // 3
192 MortonCopy<false, PixelFormat::RGBA4>, // 4
193 nullptr,
194 nullptr,
195 nullptr,
196 nullptr,
197 nullptr,
198 nullptr,
199 nullptr,
200 nullptr,
201 nullptr, // 5 - 13
202 MortonCopy<false, PixelFormat::D16>, // 14
203 nullptr, // 15
204 MortonCopy<false, PixelFormat::D24>, // 16
205 MortonCopy<false, PixelFormat::D24S8> // 17
206};
207
208// Allocate an uninitialized texture of appropriate size and format for the surface
209static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width,
210 u32 height) {
211 OpenGLState cur_state = OpenGLState::GetCurState();
212
213 // Keep track of previous texture bindings
214 GLuint old_tex = cur_state.texture_units[0].texture_2d;
215 cur_state.texture_units[0].texture_2d = texture;
216 cur_state.Apply();
217 glActiveTexture(GL_TEXTURE0);
218
219 glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
220 format_tuple.format, format_tuple.type, nullptr);
221
222 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
223 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
224 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
225 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
226
227 // Restore previous texture bindings
228 cur_state.texture_units[0].texture_2d = old_tex;
229 cur_state.Apply();
230}
231
232static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
233 const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
234 GLuint read_fb_handle, GLuint draw_fb_handle) {
235 OpenGLState state = OpenGLState::GetCurState();
236
237 OpenGLState prev_state = state;
238 SCOPE_EXIT({ prev_state.Apply(); });
239
240 // Make sure textures aren't bound to texture units, since going to bind them to framebuffer
241 // components
242 state.ResetTexture(src_tex);
243 state.ResetTexture(dst_tex);
244
245 state.draw.read_framebuffer = read_fb_handle;
246 state.draw.draw_framebuffer = draw_fb_handle;
247 state.Apply();
248
249 u32 buffers = 0;
250
251 if (type == SurfaceType::Color || type == SurfaceType::Texture) {
252 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex,
253 0);
254 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
255 0);
256
257 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex,
258 0);
259 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
260 0);
261
262 buffers = GL_COLOR_BUFFER_BIT;
263 } else if (type == SurfaceType::Depth) {
264 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
265 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0);
266 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
267
268 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
269 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0);
270 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
271
272 buffers = GL_DEPTH_BUFFER_BIT;
273 } else if (type == SurfaceType::DepthStencil) {
274 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
275 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
276 src_tex, 0);
277
278 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
279 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
280 dst_tex, 0);
281
282 buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
283 }
284
285 glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, dst_rect.left,
286 dst_rect.bottom, dst_rect.right, dst_rect.top, buffers,
287 buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
288
289 return true;
290}
291
292static bool FillSurface(const Surface& surface, const u8* fill_data,
293 const MathUtil::Rectangle<u32>& fill_rect, GLuint draw_fb_handle) {
294 UNIMPLEMENTED();
295 return true;
296}
297
298SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const {
299 SurfaceParams params = *this;
300 const u32 tiled_size = is_tiled ? 8 : 1;
301 const u64 stride_tiled_bytes = BytesInPixels(stride * tiled_size);
302 PAddr aligned_start =
303 addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes);
304 PAddr aligned_end =
305 addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes);
306
307 if (aligned_end - aligned_start > stride_tiled_bytes) {
308 params.addr = aligned_start;
309 params.height = static_cast<u32>((aligned_end - aligned_start) / BytesInPixels(stride));
310 } else {
311 // 1 row
312 ASSERT(aligned_end - aligned_start == stride_tiled_bytes);
313 const u64 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1);
314 aligned_start =
315 addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment);
316 aligned_end =
317 addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment);
318 params.addr = aligned_start;
319 params.width = static_cast<u32>(PixelsInBytes(aligned_end - aligned_start) / tiled_size);
320 params.stride = params.width;
321 params.height = tiled_size;
322 }
323 params.UpdateParams();
324
325 return params;
326}
327
328SurfaceInterval SurfaceParams::GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const {
329 if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) {
330 return {};
331 }
332
333 if (is_tiled) {
334 unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8;
335 unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8;
336 unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8;
337 unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8;
338 }
339
340 const u32 stride_tiled = !is_tiled ? stride : stride * 8;
341
342 const u32 pixel_offset =
343 stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) +
344 unscaled_rect.left;
345
346 const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth();
347
348 return {addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels)};
349}
350
351MathUtil::Rectangle<u32> SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const {
352 const u32 begin_pixel_index = static_cast<u32>(PixelsInBytes(sub_surface.addr - addr));
353
354 if (is_tiled) {
355 const int x0 = (begin_pixel_index % (stride * 8)) / 8;
356 const int y0 = (begin_pixel_index / (stride * 8)) * 8;
357 // Top to bottom
358 return MathUtil::Rectangle<u32>(x0, height - y0, x0 + sub_surface.width,
359 height - (y0 + sub_surface.height));
360 }
361
362 const int x0 = begin_pixel_index % stride;
363 const int y0 = begin_pixel_index / stride;
364 // Bottom to top
365 return MathUtil::Rectangle<u32>(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0);
366}
367
368MathUtil::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const {
369 auto rect = GetSubRect(sub_surface);
370 rect.left = rect.left * res_scale;
371 rect.right = rect.right * res_scale;
372 rect.top = rect.top * res_scale;
373 rect.bottom = rect.bottom * res_scale;
374 return rect;
375}
376
377bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
378 return std::tie(other_surface.addr, other_surface.width, other_surface.height,
379 other_surface.stride, other_surface.pixel_format, other_surface.is_tiled) ==
380 std::tie(addr, width, height, stride, pixel_format, is_tiled) &&
381 pixel_format != PixelFormat::Invalid;
382}
383
384bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const {
385 return sub_surface.addr >= addr && sub_surface.end <= end &&
386 sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid &&
387 sub_surface.is_tiled == is_tiled &&
388 (sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
389 (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) &&
390 GetSubRect(sub_surface).left + sub_surface.width <= stride;
391}
392
393bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const {
394 return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format &&
395 addr <= expanded_surface.end && expanded_surface.addr <= end &&
396 is_tiled == expanded_surface.is_tiled && stride == expanded_surface.stride &&
397 (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) %
398 BytesInPixels(stride * (is_tiled ? 8 : 1)) ==
399 0;
400}
401
402bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
403 if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr ||
404 end < texcopy_params.end) {
405 return false;
406 }
407 if (texcopy_params.width != texcopy_params.stride) {
408 const u32 tile_stride = static_cast<u32>(BytesInPixels(stride * (is_tiled ? 8 : 1)));
409 return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
410 texcopy_params.width % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
411 (texcopy_params.height == 1 || texcopy_params.stride == tile_stride) &&
412 ((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride;
413 }
414 return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval();
415}
416
417bool CachedSurface::CanFill(const SurfaceParams& dest_surface,
418 SurfaceInterval fill_interval) const {
419 if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
420 boost::icl::first(fill_interval) >= addr &&
421 boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
422 dest_surface.FromInterval(fill_interval).GetInterval() ==
423 fill_interval) { // make sure interval is a rectangle in dest surface
424 if (fill_size * 8 != dest_surface.GetFormatBpp()) {
425 // Check if bits repeat for our fill_size
426 const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / 8, 1u);
427 std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
428
429 for (u32 i = 0; i < dest_bytes_per_pixel; ++i)
430 std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
431
432 for (u32 i = 0; i < fill_size; ++i)
433 if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0],
434 dest_bytes_per_pixel) != 0)
435 return false;
436
437 if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4))
438 return false;
439 }
440 return true;
441 }
442 return false;
443}
444
445bool CachedSurface::CanCopy(const SurfaceParams& dest_surface,
446 SurfaceInterval copy_interval) const {
447 SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval);
448 ASSERT(subrect_params.GetInterval() == copy_interval);
449 if (CanSubRect(subrect_params))
450 return true;
451
452 if (CanFill(dest_surface, copy_interval))
453 return true;
454
455 return false;
456}
457
458SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) const {
459 SurfaceInterval result{};
460 const auto valid_regions =
461 SurfaceRegions(GetInterval() & src_surface->GetInterval()) - src_surface->invalid_regions;
462 for (auto& valid_interval : valid_regions) {
463 const SurfaceInterval aligned_interval{
464 addr + Common::AlignUp(boost::icl::first(valid_interval) - addr,
465 BytesInPixels(is_tiled ? 8 * 8 : 1)),
466 addr + Common::AlignDown(boost::icl::last_next(valid_interval) - addr,
467 BytesInPixels(is_tiled ? 8 * 8 : 1))};
468
469 if (BytesInPixels(is_tiled ? 8 * 8 : 1) > boost::icl::length(valid_interval) ||
470 boost::icl::length(aligned_interval) == 0) {
471 continue;
472 }
473
474 // Get the rectangle within aligned_interval
475 const u32 stride_bytes = static_cast<u32>(BytesInPixels(stride)) * (is_tiled ? 8 : 1);
476 SurfaceInterval rect_interval{
477 addr + Common::AlignUp(boost::icl::first(aligned_interval) - addr, stride_bytes),
478 addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - addr, stride_bytes),
479 };
480 if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
481 // 1 row
482 rect_interval = aligned_interval;
483 } else if (boost::icl::length(rect_interval) == 0) {
484 // 2 rows that do not make a rectangle, return the larger one
485 const SurfaceInterval row1{boost::icl::first(aligned_interval),
486 boost::icl::first(rect_interval)};
487 const SurfaceInterval row2{boost::icl::first(rect_interval),
488 boost::icl::last_next(aligned_interval)};
489 rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2;
490 }
491
492 if (boost::icl::length(rect_interval) > boost::icl::length(result)) {
493 result = rect_interval;
494 }
495 }
496 return result;
497}
498
499void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface,
500 SurfaceInterval copy_interval) {
501 SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval);
502 ASSERT(subrect_params.GetInterval() == copy_interval);
503
504 ASSERT(src_surface != dst_surface);
505
506 // This is only called when CanCopy is true, no need to run checks here
507 if (src_surface->type == SurfaceType::Fill) {
508 // FillSurface needs a 4 bytes buffer
509 const u64 fill_offset =
510 (boost::icl::first(copy_interval) - src_surface->addr) % src_surface->fill_size;
511 std::array<u8, 4> fill_buffer;
512
513 u64 fill_buff_pos = fill_offset;
514 for (int i : {0, 1, 2, 3})
515 fill_buffer[i] = src_surface->fill_data[fill_buff_pos++ % src_surface->fill_size];
516
517 FillSurface(dst_surface, &fill_buffer[0], dst_surface->GetScaledSubRect(subrect_params),
518 draw_framebuffer.handle);
519 return;
520 }
521 if (src_surface->CanSubRect(subrect_params)) {
522 BlitTextures(src_surface->texture.handle, src_surface->GetScaledSubRect(subrect_params),
523 dst_surface->texture.handle, dst_surface->GetScaledSubRect(subrect_params),
524 src_surface->type, read_framebuffer.handle, draw_framebuffer.handle);
525 return;
526 }
527 UNREACHABLE();
528}
529
530MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
531void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) {
532 ASSERT(type != SurfaceType::Fill);
533
534 const u8* const texture_src_data = Memory::GetPhysicalPointer(addr);
535 if (texture_src_data == nullptr)
536 return;
537
538 if (gl_buffer == nullptr) {
539 gl_buffer_size = width * height * GetGLBytesPerPixel(pixel_format);
540 gl_buffer.reset(new u8[gl_buffer_size]);
541 }
542
543 // TODO: Should probably be done in ::Memory:: and check for other regions too
544 if (load_start < Memory::VRAM_VADDR_END && load_end > Memory::VRAM_VADDR_END)
545 load_end = Memory::VRAM_VADDR_END;
546
547 if (load_start < Memory::VRAM_VADDR && load_end > Memory::VRAM_VADDR)
548 load_start = Memory::VRAM_VADDR;
549
550 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
551
552 ASSERT(load_start >= addr && load_end <= end);
553 const u32 start_offset = load_start - addr;
554
555 if (!is_tiled) {
556 ASSERT(type == SurfaceType::Color);
557 std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset,
558 load_end - load_start);
559 } else {
560 if (type == SurfaceType::Texture) {
561 UNIMPLEMENTED();
562 } else {
563 morton_to_gl_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr,
564 load_start, load_end);
565 }
566 }
567}
568
569MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
570void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
571 u8* const dst_buffer = Memory::GetPhysicalPointer(addr);
572 if (dst_buffer == nullptr)
573 return;
574
575 ASSERT(gl_buffer_size == width * height * GetGLBytesPerPixel(pixel_format));
576
577 // TODO: Should probably be done in ::Memory:: and check for other regions too
578 // same as loadglbuffer()
579 if (flush_start < Memory::VRAM_VADDR_END && flush_end > Memory::VRAM_VADDR_END)
580 flush_end = Memory::VRAM_VADDR_END;
581
582 if (flush_start < Memory::VRAM_VADDR && flush_end > Memory::VRAM_VADDR)
583 flush_start = Memory::VRAM_VADDR;
584
585 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
586
587 ASSERT(flush_start >= addr && flush_end <= end);
588 const u64 start_offset = flush_start - addr;
589 const u64 end_offset = flush_end - addr;
590
591 if (type == SurfaceType::Fill) {
592 const u64 coarse_start_offset = start_offset - (start_offset % fill_size);
593 const u64 backup_bytes = start_offset % fill_size;
594 std::array<u8, 4> backup_data;
595 if (backup_bytes)
596 std::memcpy(&backup_data[0], &dst_buffer[coarse_start_offset], backup_bytes);
597
598 for (u64 offset = coarse_start_offset; offset < end_offset; offset += fill_size) {
599 std::memcpy(&dst_buffer[offset], &fill_data[0],
600 std::min(fill_size, end_offset - offset));
601 }
602
603 if (backup_bytes)
604 std::memcpy(&dst_buffer[coarse_start_offset], &backup_data[0], backup_bytes);
605 } else if (!is_tiled) {
606 ASSERT(type == SurfaceType::Color);
607 std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start);
608 } else {
609 gl_to_morton_fns[static_cast<size_t>(pixel_format)](stride, height, &gl_buffer[0], addr,
610 flush_start, flush_end);
611 }
612}
613
614MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
615void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
616 GLuint draw_fb_handle) {
617 if (type == SurfaceType::Fill)
618 return;
619
620 MICROPROFILE_SCOPE(OpenGL_TextureUL);
621
622 ASSERT(gl_buffer_size == width * height * GetGLBytesPerPixel(pixel_format));
623
624 // Load data from memory to the surface
625 GLint x0 = static_cast<GLint>(rect.left);
626 GLint y0 = static_cast<GLint>(rect.bottom);
627 size_t buffer_offset = (y0 * stride + x0) * GetGLBytesPerPixel(pixel_format);
628
629 const FormatTuple& tuple = GetFormatTuple(pixel_format);
630 GLuint target_tex = texture.handle;
631
632 // If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
633 // surface
634 OGLTexture unscaled_tex;
635 if (res_scale != 1) {
636 x0 = 0;
637 y0 = 0;
638
639 unscaled_tex.Create();
640 AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight());
641 target_tex = unscaled_tex.handle;
642 }
643
644 OpenGLState cur_state = OpenGLState::GetCurState();
645
646 GLuint old_tex = cur_state.texture_units[0].texture_2d;
647 cur_state.texture_units[0].texture_2d = target_tex;
648 cur_state.Apply();
649
650 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
651 ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
652 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
653
654 glActiveTexture(GL_TEXTURE0);
655 glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
656 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
657 &gl_buffer[buffer_offset]);
658
659 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
660
661 cur_state.texture_units[0].texture_2d = old_tex;
662 cur_state.Apply();
663
664 if (res_scale != 1) {
665 auto scaled_rect = rect;
666 scaled_rect.left *= res_scale;
667 scaled_rect.top *= res_scale;
668 scaled_rect.right *= res_scale;
669 scaled_rect.bottom *= res_scale;
670
671 BlitTextures(unscaled_tex.handle, {0, rect.GetHeight(), rect.GetWidth(), 0}, texture.handle,
672 scaled_rect, type, read_fb_handle, draw_fb_handle);
673 }
674}
675
676MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64));
677void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
678 GLuint draw_fb_handle) {
679 if (type == SurfaceType::Fill)
680 return;
681
682 MICROPROFILE_SCOPE(OpenGL_TextureDL);
683
684 if (gl_buffer == nullptr) {
685 gl_buffer_size = width * height * GetGLBytesPerPixel(pixel_format);
686 gl_buffer.reset(new u8[gl_buffer_size]);
687 }
688
689 OpenGLState state = OpenGLState::GetCurState();
690 OpenGLState prev_state = state;
691 SCOPE_EXIT({ prev_state.Apply(); });
692
693 const FormatTuple& tuple = GetFormatTuple(pixel_format);
694
695 // Ensure no bad interactions with GL_PACK_ALIGNMENT
696 ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
697 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
698 size_t buffer_offset = (rect.bottom * stride + rect.left) * GetGLBytesPerPixel(pixel_format);
699
700 // If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
701 if (res_scale != 1) {
702 auto scaled_rect = rect;
703 scaled_rect.left *= res_scale;
704 scaled_rect.top *= res_scale;
705 scaled_rect.right *= res_scale;
706 scaled_rect.bottom *= res_scale;
707
708 OGLTexture unscaled_tex;
709 unscaled_tex.Create();
710
711 MathUtil::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
712 AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight());
713 BlitTextures(texture.handle, scaled_rect, unscaled_tex.handle, unscaled_tex_rect, type,
714 read_fb_handle, draw_fb_handle);
715
716 state.texture_units[0].texture_2d = unscaled_tex.handle;
717 state.Apply();
718
719 glActiveTexture(GL_TEXTURE0);
720 glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]);
721 } else {
722 state.ResetTexture(texture.handle);
723 state.draw.read_framebuffer = read_fb_handle;
724 state.Apply();
725
726 if (type == SurfaceType::Color || type == SurfaceType::Texture) {
727 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
728 texture.handle, 0);
729 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
730 0, 0);
731 } else if (type == SurfaceType::Depth) {
732 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
733 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
734 texture.handle, 0);
735 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
736 } else {
737 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
738 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
739 texture.handle, 0);
740 }
741 glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
742 static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
743 tuple.format, tuple.type, &gl_buffer[buffer_offset]);
744 }
745
746 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
747}
748
749enum MatchFlags {
750 Invalid = 1, // Flag that can be applied to other match types, invalid matches require
751 // validation before they can be used
752 Exact = 1 << 1, // Surfaces perfectly match
753 SubRect = 1 << 2, // Surface encompasses params
754 Copy = 1 << 3, // Surface we can copy from
755 Expand = 1 << 4, // Surface that can expand params
756 TexCopy = 1 << 5 // Surface that will match a display transfer "texture copy" parameters
757};
758
759constexpr MatchFlags operator|(MatchFlags lhs, MatchFlags rhs) {
760 return static_cast<MatchFlags>(static_cast<int>(lhs) | static_cast<int>(rhs));
761}
762
763/// Get the best surface match (and its match type) for the given flags
764template <MatchFlags find_flags>
765Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params,
766 ScaleMatch match_scale_type,
767 boost::optional<SurfaceInterval> validate_interval = boost::none) {
768 Surface match_surface = nullptr;
769 bool match_valid = false;
770 u32 match_scale = 0;
771 SurfaceInterval match_interval{};
772
773 for (auto& pair : RangeFromInterval(surface_cache, params.GetInterval())) {
774 for (auto& surface : pair.second) {
775 bool res_scale_matched = match_scale_type == ScaleMatch::Exact
776 ? (params.res_scale == surface->res_scale)
777 : (params.res_scale <= surface->res_scale);
778 // validity will be checked in GetCopyableInterval
779 bool is_valid =
780 find_flags & MatchFlags::Copy
781 ? true
782 : surface->IsRegionValid(validate_interval.value_or(params.GetInterval()));
783
784 if (!(find_flags & MatchFlags::Invalid) && !is_valid)
785 continue;
786
787 auto IsMatch_Helper = [&](auto check_type, auto match_fn) {
788 if (!(find_flags & check_type))
789 return;
790
791 bool matched;
792 SurfaceInterval surface_interval;
793 std::tie(matched, surface_interval) = match_fn();
794 if (!matched)
795 return;
796
797 if (!res_scale_matched && match_scale_type != ScaleMatch::Ignore &&
798 surface->type != SurfaceType::Fill)
799 return;
800
801 // Found a match, update only if this is better than the previous one
802 auto UpdateMatch = [&] {
803 match_surface = surface;
804 match_valid = is_valid;
805 match_scale = surface->res_scale;
806 match_interval = surface_interval;
807 };
808
809 if (surface->res_scale > match_scale) {
810 UpdateMatch();
811 return;
812 } else if (surface->res_scale < match_scale) {
813 return;
814 }
815
816 if (is_valid && !match_valid) {
817 UpdateMatch();
818 return;
819 } else if (is_valid != match_valid) {
820 return;
821 }
822
823 if (boost::icl::length(surface_interval) > boost::icl::length(match_interval)) {
824 UpdateMatch();
825 }
826 };
827 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Exact>{}, [&] {
828 return std::make_pair(surface->ExactMatch(params), surface->GetInterval());
829 });
830 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::SubRect>{}, [&] {
831 return std::make_pair(surface->CanSubRect(params), surface->GetInterval());
832 });
833 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Copy>{}, [&] {
834 auto copy_interval =
835 params.FromInterval(*validate_interval).GetCopyableInterval(surface);
836 bool matched = boost::icl::length(copy_interval & *validate_interval) != 0 &&
837 surface->CanCopy(params, copy_interval);
838 return std::make_pair(matched, copy_interval);
839 });
840 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::Expand>{}, [&] {
841 return std::make_pair(surface->CanExpand(params), surface->GetInterval());
842 });
843 IsMatch_Helper(std::integral_constant<MatchFlags, MatchFlags::TexCopy>{}, [&] {
844 return std::make_pair(surface->CanTexCopy(params), surface->GetInterval());
845 });
846 }
847 }
848 return match_surface;
849}
850
851RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
852 read_framebuffer.Create();
853 draw_framebuffer.Create();
854
855 attributeless_vao.Create();
856
857 d24s8_abgr_buffer.Create();
858 d24s8_abgr_buffer_size = 0;
859
860 const char* vs_source = R"(
861#version 330 core
862const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
863void main() {
864 gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
865}
866)";
867 const char* fs_source = R"(
868#version 330 core
869
870uniform samplerBuffer tbo;
871uniform vec2 tbo_size;
872uniform vec4 viewport;
873
874out vec4 color;
875
876void main() {
877 vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw;
878 int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x);
879 color = texelFetch(tbo, tbo_offset).rabg;
880}
881)";
882 d24s8_abgr_shader.Create(vs_source, nullptr, fs_source);
883
884 OpenGLState state = OpenGLState::GetCurState();
885 GLuint old_program = state.draw.shader_program;
886 state.draw.shader_program = d24s8_abgr_shader.handle;
887 state.Apply();
888
889 GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo");
890 ASSERT(tbo_u_id != -1);
891 glUniform1i(tbo_u_id, 0);
892
893 state.draw.shader_program = old_program;
894 state.Apply();
895
896 d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size");
897 ASSERT(d24s8_abgr_tbo_size_u_id != -1);
898 d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport");
899 ASSERT(d24s8_abgr_viewport_u_id != -1);
900}
901
902RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
903 FlushAll();
904 while (!surface_cache.empty())
905 UnregisterSurface(*surface_cache.begin()->second.begin());
906}
907
908bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface,
909 const MathUtil::Rectangle<u32>& src_rect,
910 const Surface& dst_surface,
911 const MathUtil::Rectangle<u32>& dst_rect) {
912 if (!SurfaceParams::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format))
913 return false;
914
915 return BlitTextures(src_surface->texture.handle, src_rect, dst_surface->texture.handle,
916 dst_rect, src_surface->type, read_framebuffer.handle,
917 draw_framebuffer.handle);
918}
919
920void RasterizerCacheOpenGL::ConvertD24S8toABGR(GLuint src_tex,
921 const MathUtil::Rectangle<u32>& src_rect,
922 GLuint dst_tex,
923 const MathUtil::Rectangle<u32>& dst_rect) {
924 OpenGLState prev_state = OpenGLState::GetCurState();
925 SCOPE_EXIT({ prev_state.Apply(); });
926
927 OpenGLState state;
928 state.draw.read_framebuffer = read_framebuffer.handle;
929 state.draw.draw_framebuffer = draw_framebuffer.handle;
930 state.Apply();
931
932 glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle);
933
934 GLsizeiptr target_pbo_size = src_rect.GetWidth() * src_rect.GetHeight() * 4;
935 if (target_pbo_size > d24s8_abgr_buffer_size) {
936 d24s8_abgr_buffer_size = target_pbo_size * 2;
937 glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY);
938 }
939
940 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
941 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, src_tex,
942 0);
943 glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom),
944 static_cast<GLsizei>(src_rect.GetWidth()),
945 static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
946 0);
947
948 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
949
950 // PBO now contains src_tex in RABG format
951 state.draw.shader_program = d24s8_abgr_shader.handle;
952 state.draw.vertex_array = attributeless_vao.handle;
953 state.viewport.x = static_cast<GLint>(dst_rect.left);
954 state.viewport.y = static_cast<GLint>(dst_rect.bottom);
955 state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth());
956 state.viewport.height = static_cast<GLsizei>(dst_rect.GetHeight());
957 state.Apply();
958
959 OGLTexture tbo;
960 tbo.Create();
961 glActiveTexture(GL_TEXTURE0);
962 glBindTexture(GL_TEXTURE_BUFFER, tbo.handle);
963 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle);
964
965 glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast<GLfloat>(src_rect.GetWidth()),
966 static_cast<GLfloat>(src_rect.GetHeight()));
967 glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x),
968 static_cast<GLfloat>(state.viewport.y), static_cast<GLfloat>(state.viewport.width),
969 static_cast<GLfloat>(state.viewport.height));
970
971 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0);
972 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
973 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
974
975 glBindTexture(GL_TEXTURE_BUFFER, 0);
976}
977
978Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
979 bool load_if_create) {
980 if (params.addr == 0 || params.height * params.width == 0) {
981 return nullptr;
982 }
983 // Use GetSurfaceSubRect instead
984 ASSERT(params.width == params.stride);
985
986 ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0));
987
988 // Check for an exact match in existing surfaces
989 Surface surface =
990 FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
991
992 if (surface == nullptr) {
993 u16 target_res_scale = params.res_scale;
994 if (match_res_scale != ScaleMatch::Exact) {
995 // This surface may have a subrect of another surface with a higher res_scale, find it
996 // to adjust our params
997 SurfaceParams find_params = params;
998 Surface expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(
999 surface_cache, find_params, match_res_scale);
1000 if (expandable != nullptr && expandable->res_scale > target_res_scale) {
1001 target_res_scale = expandable->res_scale;
1002 }
1003 // Keep res_scale when reinterpreting d24s8 -> rgba8
1004 if (params.pixel_format == PixelFormat::RGBA8) {
1005 find_params.pixel_format = PixelFormat::D24S8;
1006 expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(
1007 surface_cache, find_params, match_res_scale);
1008 if (expandable != nullptr && expandable->res_scale > target_res_scale) {
1009 target_res_scale = expandable->res_scale;
1010 }
1011 }
1012 }
1013 SurfaceParams new_params = params;
1014 new_params.res_scale = target_res_scale;
1015 surface = CreateSurface(new_params);
1016 RegisterSurface(surface);
1017 }
1018
1019 if (load_if_create) {
1020 ValidateSurface(surface, params.addr, params.size);
1021 }
1022
1023 return surface;
1024}
1025
1026SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params,
1027 ScaleMatch match_res_scale,
1028 bool load_if_create) {
1029 if (params.addr == 0 || params.height * params.width == 0) {
1030 return std::make_tuple(nullptr, MathUtil::Rectangle<u32>{});
1031 }
1032
1033 // Attempt to find encompassing surface
1034 Surface surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params,
1035 match_res_scale);
1036
1037 // Check if FindMatch failed because of res scaling
1038 // If that's the case create a new surface with
1039 // the dimensions of the lower res_scale surface
1040 // to suggest it should not be used again
1041 if (surface == nullptr && match_res_scale != ScaleMatch::Ignore) {
1042 surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params,
1043 ScaleMatch::Ignore);
1044 if (surface != nullptr) {
1045 ASSERT(surface->res_scale < params.res_scale);
1046 SurfaceParams new_params = *surface;
1047 new_params.res_scale = params.res_scale;
1048
1049 surface = CreateSurface(new_params);
1050 RegisterSurface(surface);
1051 }
1052 }
1053
1054 SurfaceParams aligned_params = params;
1055 if (params.is_tiled) {
1056 aligned_params.height = Common::AlignUp(params.height, 8);
1057 aligned_params.width = Common::AlignUp(params.width, 8);
1058 aligned_params.stride = Common::AlignUp(params.stride, 8);
1059 aligned_params.UpdateParams();
1060 }
1061
1062 // Check for a surface we can expand before creating a new one
1063 if (surface == nullptr) {
1064 surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, aligned_params,
1065 match_res_scale);
1066 if (surface != nullptr) {
1067 aligned_params.width = aligned_params.stride;
1068 aligned_params.UpdateParams();
1069
1070 SurfaceParams new_params = *surface;
1071 new_params.addr = std::min(aligned_params.addr, surface->addr);
1072 new_params.end = std::max(aligned_params.end, surface->end);
1073 new_params.size = new_params.end - new_params.addr;
1074 new_params.height = static_cast<u32>(
1075 new_params.size / aligned_params.BytesInPixels(aligned_params.stride));
1076 ASSERT(new_params.size % aligned_params.BytesInPixels(aligned_params.stride) == 0);
1077
1078 Surface new_surface = CreateSurface(new_params);
1079 DuplicateSurface(surface, new_surface);
1080
1081 // Delete the expanded surface, this can't be done safely yet
1082 // because it may still be in use
1083 remove_surfaces.emplace(surface);
1084
1085 surface = new_surface;
1086 RegisterSurface(new_surface);
1087 }
1088 }
1089
1090 // No subrect found - create and return a new surface
1091 if (surface == nullptr) {
1092 SurfaceParams new_params = aligned_params;
1093 // Can't have gaps in a surface
1094 new_params.width = aligned_params.stride;
1095 new_params.UpdateParams();
1096 // GetSurface will create the new surface and possibly adjust res_scale if necessary
1097 surface = GetSurface(new_params, match_res_scale, load_if_create);
1098 } else if (load_if_create) {
1099 ValidateSurface(surface, aligned_params.addr, aligned_params.size);
1100 }
1101
1102 return std::make_tuple(surface, surface->GetScaledSubRect(params));
1103}
1104
1105Surface RasterizerCacheOpenGL::GetTextureSurface(const void* config) {
1106 UNIMPLEMENTED();
1107 return {};
1108}
1109
1110SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
1111 bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport_rect) {
1112 UNIMPLEMENTED();
1113 return {};
1114}
1115
1116Surface RasterizerCacheOpenGL::GetFillSurface(const void* config) {
1117 UNIMPLEMENTED();
1118 return {};
1119}
1120
1121SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams& params) {
1122 MathUtil::Rectangle<u32> rect{};
1123
1124 Surface match_surface = FindMatch<MatchFlags::TexCopy | MatchFlags::Invalid>(
1125 surface_cache, params, ScaleMatch::Ignore);
1126
1127 if (match_surface != nullptr) {
1128 ValidateSurface(match_surface, params.addr, params.size);
1129
1130 SurfaceParams match_subrect;
1131 if (params.width != params.stride) {
1132 const u32 tiled_size = match_surface->is_tiled ? 8 : 1;
1133 match_subrect = params;
1134 match_subrect.width =
1135 static_cast<u32>(match_surface->PixelsInBytes(params.width) / tiled_size);
1136 match_subrect.stride =
1137 static_cast<u32>(match_surface->PixelsInBytes(params.stride) / tiled_size);
1138 match_subrect.height *= tiled_size;
1139 } else {
1140 match_subrect = match_surface->FromInterval(params.GetInterval());
1141 ASSERT(match_subrect.GetInterval() == params.GetInterval());
1142 }
1143
1144 rect = match_surface->GetScaledSubRect(match_subrect);
1145 }
1146
1147 return std::make_tuple(match_surface, rect);
1148}
1149
1150void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface,
1151 const Surface& dest_surface) {
1152 ASSERT(dest_surface->addr <= src_surface->addr && dest_surface->end >= src_surface->end);
1153
1154 BlitSurfaces(src_surface, src_surface->GetScaledRect(), dest_surface,
1155 dest_surface->GetScaledSubRect(*src_surface));
1156
1157 dest_surface->invalid_regions -= src_surface->GetInterval();
1158 dest_surface->invalid_regions += src_surface->invalid_regions;
1159
1160 SurfaceRegions regions;
1161 for (auto& pair : RangeFromInterval(dirty_regions, src_surface->GetInterval())) {
1162 if (pair.second == src_surface) {
1163 regions += pair.first;
1164 }
1165 }
1166 for (auto& interval : regions) {
1167 dirty_regions.set({interval, dest_surface});
1168 }
1169}
1170
1171void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, u64 size) {
1172 if (size == 0)
1173 return;
1174
1175 const SurfaceInterval validate_interval(addr, addr + size);
1176
1177 if (surface->type == SurfaceType::Fill) {
1178 // Sanity check, fill surfaces will always be valid when used
1179 ASSERT(surface->IsRegionValid(validate_interval));
1180 return;
1181 }
1182
1183 while (true) {
1184 const auto it = surface->invalid_regions.find(validate_interval);
1185 if (it == surface->invalid_regions.end())
1186 break;
1187
1188 const auto interval = *it & validate_interval;
1189 // Look for a valid surface to copy from
1190 SurfaceParams params = surface->FromInterval(interval);
1191
1192 Surface copy_surface =
1193 FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
1194 if (copy_surface != nullptr) {
1195 SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface);
1196 CopySurface(copy_surface, surface, copy_interval);
1197 surface->invalid_regions.erase(copy_interval);
1198 continue;
1199 }
1200
1201 // D24S8 to RGBA8
1202 if (surface->pixel_format == PixelFormat::RGBA8) {
1203 params.pixel_format = PixelFormat::D24S8;
1204 Surface reinterpret_surface =
1205 FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
1206 if (reinterpret_surface != nullptr) {
1207 ASSERT(reinterpret_surface->pixel_format == PixelFormat::D24S8);
1208
1209 SurfaceInterval convert_interval = params.GetCopyableInterval(reinterpret_surface);
1210 SurfaceParams convert_params = surface->FromInterval(convert_interval);
1211 auto src_rect = reinterpret_surface->GetScaledSubRect(convert_params);
1212 auto dest_rect = surface->GetScaledSubRect(convert_params);
1213
1214 ConvertD24S8toABGR(reinterpret_surface->texture.handle, src_rect,
1215 surface->texture.handle, dest_rect);
1216
1217 surface->invalid_regions.erase(convert_interval);
1218 continue;
1219 }
1220 }
1221
1222 // Load data from 3DS memory
1223 FlushRegion(params.addr, params.size);
1224 surface->LoadGLBuffer(params.addr, params.end);
1225 surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
1226 draw_framebuffer.handle);
1227 surface->invalid_regions.erase(params.GetInterval());
1228 }
1229}
1230
1231void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u64 size, Surface flush_surface) {
1232 if (size == 0)
1233 return;
1234
1235 const SurfaceInterval flush_interval(addr, addr + size);
1236 SurfaceRegions flushed_intervals;
1237
1238 for (auto& pair : RangeFromInterval(dirty_regions, flush_interval)) {
1239 // small sizes imply that this most likely comes from the cpu, flush the entire region
1240 // the point is to avoid thousands of small writes every frame if the cpu decides to access
1241 // that region, anything higher than 8 you're guaranteed it comes from a service
1242 const auto interval = size <= 8 ? pair.first : pair.first & flush_interval;
1243 auto& surface = pair.second;
1244
1245 if (flush_surface != nullptr && surface != flush_surface)
1246 continue;
1247
1248 // Sanity check, this surface is the last one that marked this region dirty
1249 ASSERT(surface->IsRegionValid(interval));
1250
1251 if (surface->type != SurfaceType::Fill) {
1252 SurfaceParams params = surface->FromInterval(interval);
1253 surface->DownloadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
1254 draw_framebuffer.handle);
1255 }
1256 surface->FlushGLBuffer(boost::icl::first(interval), boost::icl::last_next(interval));
1257 flushed_intervals += interval;
1258 }
1259 // Reset dirty regions
1260 dirty_regions -= flushed_intervals;
1261}
1262
1263void RasterizerCacheOpenGL::FlushAll() {
1264 FlushRegion(0, 0xFFFFFFFF);
1265}
1266
1267void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u64 size, const Surface& region_owner) {
1268 if (size == 0)
1269 return;
1270
1271 const SurfaceInterval invalid_interval(addr, addr + size);
1272
1273 if (region_owner != nullptr) {
1274 ASSERT(region_owner->type != SurfaceType::Texture);
1275 ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end);
1276 // Surfaces can't have a gap
1277 ASSERT(region_owner->width == region_owner->stride);
1278 region_owner->invalid_regions.erase(invalid_interval);
1279 }
1280
1281 for (auto& pair : RangeFromInterval(surface_cache, invalid_interval)) {
1282 for (auto& cached_surface : pair.second) {
1283 if (cached_surface == region_owner)
1284 continue;
1285
1286 // If cpu is invalidating this region we want to remove it
1287 // to (likely) mark the memory pages as uncached
1288 if (region_owner == nullptr && size <= 8) {
1289 FlushRegion(cached_surface->addr, cached_surface->size, cached_surface);
1290 remove_surfaces.emplace(cached_surface);
1291 continue;
1292 }
1293
1294 const auto interval = cached_surface->GetInterval() & invalid_interval;
1295 cached_surface->invalid_regions.insert(interval);
1296
1297 // Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
1298 if (cached_surface->type == SurfaceType::Fill &&
1299 cached_surface->IsSurfaceFullyInvalid()) {
1300 remove_surfaces.emplace(cached_surface);
1301 }
1302 }
1303 }
1304
1305 if (region_owner != nullptr)
1306 dirty_regions.set({invalid_interval, region_owner});
1307 else
1308 dirty_regions.erase(invalid_interval);
1309
1310 for (auto& remove_surface : remove_surfaces) {
1311 if (remove_surface == region_owner) {
1312 Surface expanded_surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(
1313 surface_cache, *region_owner, ScaleMatch::Ignore);
1314 ASSERT(expanded_surface);
1315
1316 if ((region_owner->invalid_regions - expanded_surface->invalid_regions).empty()) {
1317 DuplicateSurface(region_owner, expanded_surface);
1318 } else {
1319 continue;
1320 }
1321 }
1322 UnregisterSurface(remove_surface);
1323 }
1324
1325 remove_surfaces.clear();
1326}
1327
1328Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
1329 Surface surface = std::make_shared<CachedSurface>();
1330 static_cast<SurfaceParams&>(*surface) = params;
1331
1332 surface->texture.Create();
1333
1334 surface->gl_buffer_size = 0;
1335 surface->invalid_regions.insert(surface->GetInterval());
1336 AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format),
1337 surface->GetScaledWidth(), surface->GetScaledHeight());
1338
1339 return surface;
1340}
1341
1342void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) {
1343 if (surface->registered) {
1344 return;
1345 }
1346 surface->registered = true;
1347 surface_cache.add({surface->GetInterval(), SurfaceSet{surface}});
1348 UpdatePagesCachedCount(surface->addr, surface->size, 1);
1349}
1350
1351void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
1352 if (!surface->registered) {
1353 return;
1354 }
1355 surface->registered = false;
1356 UpdatePagesCachedCount(surface->addr, surface->size, -1);
1357 surface_cache.subtract({surface->GetInterval(), SurfaceSet{surface}});
1358}
1359
1360void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u64 size, int delta) {
1361 UNIMPLEMENTED();
1362}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
new file mode 100644
index 000000000..17ce0fee7
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -0,0 +1,350 @@
1// Copyright 2015 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 <array>
8#include <memory>
9#include <set>
10#include <tuple>
11#ifdef __GNUC__
12#pragma GCC diagnostic push
13#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
14#endif
15#include <boost/icl/interval_map.hpp>
16#include <boost/icl/interval_set.hpp>
17#ifdef __GNUC__
18#pragma GCC diagnostic pop
19#endif
20#include <glad/glad.h>
21#include "common/assert.h"
22#include "common/common_funcs.h"
23#include "common/common_types.h"
24#include "common/math_util.h"
25#include "video_core/renderer_opengl/gl_resource_manager.h"
26
27struct CachedSurface;
28using Surface = std::shared_ptr<CachedSurface>;
29using SurfaceSet = std::set<Surface>;
30
31using SurfaceRegions = boost::icl::interval_set<PAddr>;
32using SurfaceMap = boost::icl::interval_map<PAddr, Surface>;
33using SurfaceCache = boost::icl::interval_map<PAddr, SurfaceSet>;
34
35using SurfaceInterval = SurfaceCache::interval_type;
36static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() &&
37 std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(),
38 "incorrect interval types");
39
40using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>;
41using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
42
43using PageMap = boost::icl::interval_map<u32, int>;
44
45enum class ScaleMatch {
46 Exact, // only accept same res scale
47 Upscale, // only allow higher scale than params
48 Ignore // accept every scaled res
49};
50
51struct SurfaceParams {
52 enum class PixelFormat {
53 // First 5 formats are shared between textures and color buffers
54 RGBA8 = 0,
55 RGB8 = 1,
56 RGB5A1 = 2,
57 RGB565 = 3,
58 RGBA4 = 4,
59
60 // Texture-only formats
61 IA8 = 5,
62 RG8 = 6,
63 I8 = 7,
64 A8 = 8,
65 IA4 = 9,
66 I4 = 10,
67 A4 = 11,
68 ETC1 = 12,
69 ETC1A4 = 13,
70
71 // Depth buffer-only formats
72 D16 = 14,
73 // gap
74 D24 = 16,
75 D24S8 = 17,
76
77 Invalid = 255,
78 };
79
80 enum class SurfaceType {
81 Color = 0,
82 Texture = 1,
83 Depth = 2,
84 DepthStencil = 3,
85 Fill = 4,
86 Invalid = 5
87 };
88
89 static constexpr unsigned int GetFormatBpp(PixelFormat format) {
90 constexpr std::array<unsigned int, 18> bpp_table = {
91 32, // RGBA8
92 24, // RGB8
93 16, // RGB5A1
94 16, // RGB565
95 16, // RGBA4
96 16, // IA8
97 16, // RG8
98 8, // I8
99 8, // A8
100 8, // IA4
101 4, // I4
102 4, // A4
103 4, // ETC1
104 8, // ETC1A4
105 16, // D16
106 0,
107 24, // D24
108 32, // D24S8
109 };
110
111 assert(static_cast<size_t>(format) < bpp_table.size());
112 return bpp_table[static_cast<size_t>(format)];
113 }
114 unsigned int GetFormatBpp() const {
115 return GetFormatBpp(pixel_format);
116 }
117
118 static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) {
119 SurfaceType a_type = GetFormatType(pixel_format_a);
120 SurfaceType b_type = GetFormatType(pixel_format_b);
121
122 if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) &&
123 (b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) {
124 return true;
125 }
126
127 if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) {
128 return true;
129 }
130
131 if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) {
132 return true;
133 }
134
135 return false;
136 }
137
138 static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
139 if ((unsigned int)pixel_format < 5) {
140 return SurfaceType::Color;
141 }
142
143 if ((unsigned int)pixel_format < 14) {
144 return SurfaceType::Texture;
145 }
146
147 if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
148 return SurfaceType::Depth;
149 }
150
151 if (pixel_format == PixelFormat::D24S8) {
152 return SurfaceType::DepthStencil;
153 }
154
155 return SurfaceType::Invalid;
156 }
157
158 /// Update the params "size", "end" and "type" from the already set "addr", "width", "height"
159 /// and "pixel_format"
160 void UpdateParams() {
161 if (stride == 0) {
162 stride = width;
163 }
164 type = GetFormatType(pixel_format);
165 size = !is_tiled ? BytesInPixels(stride * (height - 1) + width)
166 : BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8);
167 end = addr + size;
168 }
169
170 SurfaceInterval GetInterval() const {
171 return SurfaceInterval::right_open(addr, end);
172 }
173
174 // Returns the outer rectangle containing "interval"
175 SurfaceParams FromInterval(SurfaceInterval interval) const;
176
177 SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const;
178
179 // Returns the region of the biggest valid rectange within interval
180 SurfaceInterval GetCopyableInterval(const Surface& src_surface) const;
181
182 u32 GetScaledWidth() const {
183 return width * res_scale;
184 }
185
186 u32 GetScaledHeight() const {
187 return height * res_scale;
188 }
189
190 MathUtil::Rectangle<u32> GetRect() const {
191 return {0, height, width, 0};
192 }
193
194 MathUtil::Rectangle<u32> GetScaledRect() const {
195 return {0, GetScaledHeight(), GetScaledWidth(), 0};
196 }
197
198 u64 PixelsInBytes(u64 size) const {
199 return size * CHAR_BIT / GetFormatBpp(pixel_format);
200 }
201
202 u64 BytesInPixels(u64 pixels) const {
203 return pixels * GetFormatBpp(pixel_format) / CHAR_BIT;
204 }
205
206 bool ExactMatch(const SurfaceParams& other_surface) const;
207 bool CanSubRect(const SurfaceParams& sub_surface) const;
208 bool CanExpand(const SurfaceParams& expanded_surface) const;
209 bool CanTexCopy(const SurfaceParams& texcopy_params) const;
210
211 MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
212 MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const;
213
214 PAddr addr = 0;
215 PAddr end = 0;
216 u64 size = 0;
217
218 u32 width = 0;
219 u32 height = 0;
220 u32 stride = 0;
221 u16 res_scale = 1;
222
223 bool is_tiled = false;
224 PixelFormat pixel_format = PixelFormat::Invalid;
225 SurfaceType type = SurfaceType::Invalid;
226};
227
228struct CachedSurface : SurfaceParams {
229 bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
230 bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
231
232 bool IsRegionValid(SurfaceInterval interval) const {
233 return (invalid_regions.find(interval) == invalid_regions.end());
234 }
235
236 bool IsSurfaceFullyInvalid() const {
237 return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval());
238 }
239
240 bool registered = false;
241 SurfaceRegions invalid_regions;
242
243 u64 fill_size = 0; /// Number of bytes to read from fill_data
244 std::array<u8, 4> fill_data;
245
246 OGLTexture texture;
247
248 static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
249 // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
250 return format == PixelFormat::Invalid
251 ? 0
252 : (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture)
253 ? 4
254 : SurfaceParams::GetFormatBpp(format) / 8;
255 }
256
257 std::unique_ptr<u8[]> gl_buffer;
258 size_t gl_buffer_size = 0;
259
260 // Read/Write data in 3DS memory to/from gl_buffer
261 void LoadGLBuffer(PAddr load_start, PAddr load_end);
262 void FlushGLBuffer(PAddr flush_start, PAddr flush_end);
263
264 // Upload/Download data in gl_buffer in/to this surface's texture
265 void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
266 GLuint draw_fb_handle);
267 void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle,
268 GLuint draw_fb_handle);
269};
270
271class RasterizerCacheOpenGL : NonCopyable {
272public:
273 RasterizerCacheOpenGL();
274 ~RasterizerCacheOpenGL();
275
276 /// Blit one surface's texture to another
277 bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect,
278 const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect);
279
280 void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect,
281 GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect);
282
283 /// Copy one surface's region to another
284 void CopySurface(const Surface& src_surface, const Surface& dst_surface,
285 SurfaceInterval copy_interval);
286
287 /// Load a texture from 3DS memory to OpenGL and cache it (if not already cached)
288 Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
289 bool load_if_create);
290
291 /// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
292 /// 3DS memory to OpenGL and caches it (if not already cached)
293 SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale,
294 bool load_if_create);
295
296 /// Get a surface based on the texture configuration
297 Surface GetTextureSurface(const void* config);
298
299 /// Get the color and depth surfaces based on the framebuffer configuration
300 SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
301 const MathUtil::Rectangle<s32>& viewport_rect);
302
303 /// Get a surface that matches the fill config
304 Surface GetFillSurface(const void* config);
305
306 /// Get a surface that matches a "texture copy" display transfer config
307 SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
308
309 /// Write any cached resources overlapping the region back to memory (if dirty)
310 void FlushRegion(PAddr addr, u64 size, Surface flush_surface = nullptr);
311
312 /// Mark region as being invalidated by region_owner (nullptr if 3DS memory)
313 void InvalidateRegion(PAddr addr, u64 size, const Surface& region_owner);
314
315 /// Flush all cached resources tracked by this cache manager
316 void FlushAll();
317
318private:
319 void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
320
321 /// Update surface's texture for given region when necessary
322 void ValidateSurface(const Surface& surface, PAddr addr, u64 size);
323
324 /// Create a new surface
325 Surface CreateSurface(const SurfaceParams& params);
326
327 /// Register surface into the cache
328 void RegisterSurface(const Surface& surface);
329
330 /// Remove surface from the cache
331 void UnregisterSurface(const Surface& surface);
332
333 /// Increase/decrease the number of surface in pages touching the specified region
334 void UpdatePagesCachedCount(PAddr addr, u64 size, int delta);
335
336 SurfaceCache surface_cache;
337 PageMap cached_pages;
338 SurfaceMap dirty_regions;
339 SurfaceSet remove_surfaces;
340
341 OGLFramebuffer read_framebuffer;
342 OGLFramebuffer draw_framebuffer;
343
344 OGLVertexArray attributeless_vao;
345 OGLBuffer d24s8_abgr_buffer;
346 GLsizeiptr d24s8_abgr_buffer_size;
347 OGLShader d24s8_abgr_shader;
348 GLint d24s8_abgr_tbo_size_u_id;
349 GLint d24s8_abgr_viewport_u_id;
350};