diff options
Diffstat (limited to 'src/video_core/renderer_opengl/gl_rasterizer_cache.h')
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 350 |
1 files changed, 350 insertions, 0 deletions
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 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> +#include <set> +#include <tuple> +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#endif +#include <boost/icl/interval_map.hpp> +#include <boost/icl/interval_set.hpp> +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#include <glad/glad.h> +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/math_util.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +struct CachedSurface; +using Surface = std::shared_ptr<CachedSurface>; +using SurfaceSet = std::set<Surface>; + +using SurfaceRegions = boost::icl::interval_set<PAddr>; +using SurfaceMap = boost::icl::interval_map<PAddr, Surface>; +using SurfaceCache = boost::icl::interval_map<PAddr, SurfaceSet>; + +using SurfaceInterval = SurfaceCache::interval_type; +static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() && + std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(), + "incorrect interval types"); + +using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>; +using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; + +using PageMap = boost::icl::interval_map<u32, int>; + +enum class ScaleMatch { + Exact, // only accept same res scale + Upscale, // only allow higher scale than params + Ignore // accept every scaled res +}; + +struct SurfaceParams { + enum class PixelFormat { + // First 5 formats are shared between textures and color buffers + RGBA8 = 0, + RGB8 = 1, + RGB5A1 = 2, + RGB565 = 3, + RGBA4 = 4, + + // Texture-only formats + IA8 = 5, + RG8 = 6, + I8 = 7, + A8 = 8, + IA4 = 9, + I4 = 10, + A4 = 11, + ETC1 = 12, + ETC1A4 = 13, + + // Depth buffer-only formats + D16 = 14, + // gap + D24 = 16, + D24S8 = 17, + + Invalid = 255, + }; + + enum class SurfaceType { + Color = 0, + Texture = 1, + Depth = 2, + DepthStencil = 3, + Fill = 4, + Invalid = 5 + }; + + static constexpr unsigned int GetFormatBpp(PixelFormat format) { + constexpr std::array<unsigned int, 18> bpp_table = { + 32, // RGBA8 + 24, // RGB8 + 16, // RGB5A1 + 16, // RGB565 + 16, // RGBA4 + 16, // IA8 + 16, // RG8 + 8, // I8 + 8, // A8 + 8, // IA4 + 4, // I4 + 4, // A4 + 4, // ETC1 + 8, // ETC1A4 + 16, // D16 + 0, + 24, // D24 + 32, // D24S8 + }; + + assert(static_cast<size_t>(format) < bpp_table.size()); + return bpp_table[static_cast<size_t>(format)]; + } + unsigned int GetFormatBpp() const { + return GetFormatBpp(pixel_format); + } + + static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { + SurfaceType a_type = GetFormatType(pixel_format_a); + SurfaceType b_type = GetFormatType(pixel_format_b); + + if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) && + (b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) { + return true; + } + + if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { + return true; + } + + if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) { + return true; + } + + return false; + } + + static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { + if ((unsigned int)pixel_format < 5) { + return SurfaceType::Color; + } + + if ((unsigned int)pixel_format < 14) { + return SurfaceType::Texture; + } + + if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) { + return SurfaceType::Depth; + } + + if (pixel_format == PixelFormat::D24S8) { + return SurfaceType::DepthStencil; + } + + return SurfaceType::Invalid; + } + + /// Update the params "size", "end" and "type" from the already set "addr", "width", "height" + /// and "pixel_format" + void UpdateParams() { + if (stride == 0) { + stride = width; + } + type = GetFormatType(pixel_format); + size = !is_tiled ? BytesInPixels(stride * (height - 1) + width) + : BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8); + end = addr + size; + } + + SurfaceInterval GetInterval() const { + return SurfaceInterval::right_open(addr, end); + } + + // Returns the outer rectangle containing "interval" + SurfaceParams FromInterval(SurfaceInterval interval) const; + + SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const; + + // Returns the region of the biggest valid rectange within interval + SurfaceInterval GetCopyableInterval(const Surface& src_surface) const; + + u32 GetScaledWidth() const { + return width * res_scale; + } + + u32 GetScaledHeight() const { + return height * res_scale; + } + + MathUtil::Rectangle<u32> GetRect() const { + return {0, height, width, 0}; + } + + MathUtil::Rectangle<u32> GetScaledRect() const { + return {0, GetScaledHeight(), GetScaledWidth(), 0}; + } + + u64 PixelsInBytes(u64 size) const { + return size * CHAR_BIT / GetFormatBpp(pixel_format); + } + + u64 BytesInPixels(u64 pixels) const { + return pixels * GetFormatBpp(pixel_format) / CHAR_BIT; + } + + bool ExactMatch(const SurfaceParams& other_surface) const; + bool CanSubRect(const SurfaceParams& sub_surface) const; + bool CanExpand(const SurfaceParams& expanded_surface) const; + bool CanTexCopy(const SurfaceParams& texcopy_params) const; + + MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const; + MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const; + + PAddr addr = 0; + PAddr end = 0; + u64 size = 0; + + u32 width = 0; + u32 height = 0; + u32 stride = 0; + u16 res_scale = 1; + + bool is_tiled = false; + PixelFormat pixel_format = PixelFormat::Invalid; + SurfaceType type = SurfaceType::Invalid; +}; + +struct CachedSurface : SurfaceParams { + bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; + bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; + + bool IsRegionValid(SurfaceInterval interval) const { + return (invalid_regions.find(interval) == invalid_regions.end()); + } + + bool IsSurfaceFullyInvalid() const { + return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval()); + } + + bool registered = false; + SurfaceRegions invalid_regions; + + u64 fill_size = 0; /// Number of bytes to read from fill_data + std::array<u8, 4> fill_data; + + OGLTexture texture; + + static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) { + // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type + return format == PixelFormat::Invalid + ? 0 + : (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) + ? 4 + : SurfaceParams::GetFormatBpp(format) / 8; + } + + std::unique_ptr<u8[]> gl_buffer; + size_t gl_buffer_size = 0; + + // Read/Write data in 3DS memory to/from gl_buffer + void LoadGLBuffer(PAddr load_start, PAddr load_end); + void FlushGLBuffer(PAddr flush_start, PAddr flush_end); + + // Upload/Download data in gl_buffer in/to this surface's texture + void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, + GLuint draw_fb_handle); + void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, + GLuint draw_fb_handle); +}; + +class RasterizerCacheOpenGL : NonCopyable { +public: + RasterizerCacheOpenGL(); + ~RasterizerCacheOpenGL(); + + /// Blit one surface's texture to another + bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect, + const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect); + + void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, + GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect); + + /// Copy one surface's region to another + void CopySurface(const Surface& src_surface, const Surface& dst_surface, + SurfaceInterval copy_interval); + + /// Load a texture from 3DS memory to OpenGL and cache it (if not already cached) + Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, + bool load_if_create); + + /// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from + /// 3DS memory to OpenGL and caches it (if not already cached) + SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale, + bool load_if_create); + + /// Get a surface based on the texture configuration + Surface GetTextureSurface(const void* config); + + /// Get the color and depth surfaces based on the framebuffer configuration + SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, + const MathUtil::Rectangle<s32>& viewport_rect); + + /// Get a surface that matches the fill config + Surface GetFillSurface(const void* config); + + /// Get a surface that matches a "texture copy" display transfer config + SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params); + + /// Write any cached resources overlapping the region back to memory (if dirty) + void FlushRegion(PAddr addr, u64 size, Surface flush_surface = nullptr); + + /// Mark region as being invalidated by region_owner (nullptr if 3DS memory) + void InvalidateRegion(PAddr addr, u64 size, const Surface& region_owner); + + /// Flush all cached resources tracked by this cache manager + void FlushAll(); + +private: + void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface); + + /// Update surface's texture for given region when necessary + void ValidateSurface(const Surface& surface, PAddr addr, u64 size); + + /// Create a new surface + Surface CreateSurface(const SurfaceParams& params); + + /// Register surface into the cache + void RegisterSurface(const Surface& surface); + + /// Remove surface from the cache + void UnregisterSurface(const Surface& surface); + + /// Increase/decrease the number of surface in pages touching the specified region + void UpdatePagesCachedCount(PAddr addr, u64 size, int delta); + + SurfaceCache surface_cache; + PageMap cached_pages; + SurfaceMap dirty_regions; + SurfaceSet remove_surfaces; + + OGLFramebuffer read_framebuffer; + OGLFramebuffer draw_framebuffer; + + OGLVertexArray attributeless_vao; + OGLBuffer d24s8_abgr_buffer; + GLsizeiptr d24s8_abgr_buffer_size; + OGLShader d24s8_abgr_shader; + GLint d24s8_abgr_tbo_size_u_id; + GLint d24s8_abgr_viewport_u_id; +}; |