mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	rasterizer_cache: Introduce TextureRuntime and separate CachedSurface
* This commit aims to both continue the rasterizer cache cleanup by separating CachedSurface into a dedicated header and to start weeding out the raw OpenGL code from the cache. * The latter is achieved by abstracting most texture operations in a new class called TextureRuntime. This has many benefits such as making it easier to port the functionality to other graphics APIs and the removal of the need to pass (read/draw) framebuffer handles everywhere. The filterer and reinterpreter get their own sets of FBOs due to this, something that might be a performance win since it reduces the state switching overhead on the runtime FBOs.
This commit is contained in:
		
							parent
							
								
									199671301d
								
							
						
					
					
						commit
						17ad594a62
					
				
					 23 changed files with 1176 additions and 1012 deletions
				
			
		|  | @ -23,6 +23,8 @@ add_library(video_core STATIC | ||||||
|     regs_texturing.h |     regs_texturing.h | ||||||
|     renderer_base.cpp |     renderer_base.cpp | ||||||
|     renderer_base.h |     renderer_base.h | ||||||
|  |     rasterizer_cache/cached_surface.cpp | ||||||
|  |     rasterizer_cache/cached_surface.h | ||||||
|     rasterizer_cache/morton_swizzle.h |     rasterizer_cache/morton_swizzle.h | ||||||
|     rasterizer_cache/pixel_format.h |     rasterizer_cache/pixel_format.h | ||||||
|     rasterizer_cache/rasterizer_cache.cpp |     rasterizer_cache/rasterizer_cache.cpp | ||||||
|  | @ -32,6 +34,8 @@ add_library(video_core STATIC | ||||||
|     rasterizer_cache/rasterizer_cache_utils.h |     rasterizer_cache/rasterizer_cache_utils.h | ||||||
|     rasterizer_cache/surface_params.cpp |     rasterizer_cache/surface_params.cpp | ||||||
|     rasterizer_cache/surface_params.h |     rasterizer_cache/surface_params.h | ||||||
|  |     rasterizer_cache/texture_runtime.cpp | ||||||
|  |     rasterizer_cache/texture_runtime.h | ||||||
|     renderer_opengl/frame_dumper_opengl.cpp |     renderer_opengl/frame_dumper_opengl.cpp | ||||||
|     renderer_opengl/frame_dumper_opengl.h |     renderer_opengl/frame_dumper_opengl.h | ||||||
|     renderer_opengl/gl_rasterizer.cpp |     renderer_opengl/gl_rasterizer.cpp | ||||||
|  |  | ||||||
							
								
								
									
										479
									
								
								src/video_core/rasterizer_cache/cached_surface.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										479
									
								
								src/video_core/rasterizer_cache/cached_surface.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,479 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "common/microprofile.h" | ||||||
|  | #include "common/texture.h" | ||||||
|  | #include "common/scope_exit.h" | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "video_core/rasterizer_cache/cached_surface.h" | ||||||
|  | #include "video_core/rasterizer_cache/morton_swizzle.h" | ||||||
|  | #include "video_core/rasterizer_cache/rasterizer_cache.h" | ||||||
|  | #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_state.h" | ||||||
|  | #include "video_core/renderer_opengl/texture_downloader_es.h" | ||||||
|  | 
 | ||||||
|  | namespace OpenGL { | ||||||
|  | 
 | ||||||
|  | static Aspect ToAspect(SurfaceType type) { | ||||||
|  |     switch (type) { | ||||||
|  |     case SurfaceType::Color: | ||||||
|  |     case SurfaceType::Texture: | ||||||
|  |     case SurfaceType::Fill: | ||||||
|  |         return Aspect::Color; | ||||||
|  |     case SurfaceType::Depth: | ||||||
|  |         return Aspect::Depth; | ||||||
|  |     case SurfaceType::DepthStencil: | ||||||
|  |         return Aspect::DepthStencil; | ||||||
|  |     default: | ||||||
|  |         LOG_CRITICAL(Render_OpenGL, "Unknown SurfaceType {}", type); | ||||||
|  |         UNREACHABLE(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return Aspect::Color; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CachedSurface::~CachedSurface() { | ||||||
|  |     if (texture.handle) { | ||||||
|  |         auto tag = is_custom ? HostTextureTag{GetFormatTuple(PixelFormat::RGBA8), | ||||||
|  |                                               custom_tex_info.width, custom_tex_info.height} | ||||||
|  |                              : HostTextureTag{GetFormatTuple(pixel_format), GetScaledWidth(), | ||||||
|  |                                               GetScaledHeight()}; | ||||||
|  | 
 | ||||||
|  |         owner.host_texture_recycler.emplace(tag, std::move(texture)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); | ||||||
|  | void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) { | ||||||
|  |     ASSERT(type != SurfaceType::Fill); | ||||||
|  |     const bool need_swap = | ||||||
|  |         GLES && (pixel_format == PixelFormat::RGBA8 || pixel_format == PixelFormat::RGB8); | ||||||
|  | 
 | ||||||
|  |     const u8* const texture_src_data = VideoCore::g_memory->GetPhysicalPointer(addr); | ||||||
|  |     if (texture_src_data == nullptr) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (gl_buffer.empty()) { | ||||||
|  |         gl_buffer.resize(width * height * GetBytesPerPixel(pixel_format)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO: Should probably be done in ::Memory:: and check for other regions too
 | ||||||
|  |     if (load_start < Memory::VRAM_VADDR_END && load_end > Memory::VRAM_VADDR_END) | ||||||
|  |         load_end = Memory::VRAM_VADDR_END; | ||||||
|  | 
 | ||||||
|  |     if (load_start < Memory::VRAM_VADDR && load_end > Memory::VRAM_VADDR) | ||||||
|  |         load_start = Memory::VRAM_VADDR; | ||||||
|  | 
 | ||||||
|  |     MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); | ||||||
|  | 
 | ||||||
|  |     ASSERT(load_start >= addr && load_end <= end); | ||||||
|  |     const u32 start_offset = load_start - addr; | ||||||
|  | 
 | ||||||
|  |     if (!is_tiled) { | ||||||
|  |         ASSERT(type == SurfaceType::Color); | ||||||
|  |         if (need_swap) { | ||||||
|  |             // TODO(liushuyu): check if the byteswap here is 100% correct
 | ||||||
|  |             // cannot fully test this
 | ||||||
|  |             if (pixel_format == PixelFormat::RGBA8) { | ||||||
|  |                 for (std::size_t i = start_offset; i < load_end - addr; i += 4) { | ||||||
|  |                     gl_buffer[i] = texture_src_data[i + 3]; | ||||||
|  |                     gl_buffer[i + 1] = texture_src_data[i + 2]; | ||||||
|  |                     gl_buffer[i + 2] = texture_src_data[i + 1]; | ||||||
|  |                     gl_buffer[i + 3] = texture_src_data[i]; | ||||||
|  |                 } | ||||||
|  |             } else if (pixel_format == PixelFormat::RGB8) { | ||||||
|  |                 for (std::size_t i = start_offset; i < load_end - addr; i += 3) { | ||||||
|  |                     gl_buffer[i] = texture_src_data[i + 2]; | ||||||
|  |                     gl_buffer[i + 1] = texture_src_data[i + 1]; | ||||||
|  |                     gl_buffer[i + 2] = texture_src_data[i]; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset, | ||||||
|  |                         load_end - load_start); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if (type == SurfaceType::Texture) { | ||||||
|  |             Pica::Texture::TextureInfo tex_info{}; | ||||||
|  |             tex_info.width = width; | ||||||
|  |             tex_info.height = height; | ||||||
|  |             tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(pixel_format); | ||||||
|  |             tex_info.SetDefaultStride(); | ||||||
|  |             tex_info.physical_address = addr; | ||||||
|  | 
 | ||||||
|  |             const SurfaceInterval load_interval(load_start, load_end); | ||||||
|  |             const auto rect = GetSubRect(FromInterval(load_interval)); | ||||||
|  |             ASSERT(FromInterval(load_interval).GetInterval() == load_interval); | ||||||
|  | 
 | ||||||
|  |             for (unsigned y = rect.bottom; y < rect.top; ++y) { | ||||||
|  |                 for (unsigned x = rect.left; x < rect.right; ++x) { | ||||||
|  |                     auto vec4 = | ||||||
|  |                         Pica::Texture::LookupTexture(texture_src_data, x, height - 1 - y, tex_info); | ||||||
|  |                     const std::size_t offset = (x + (width * y)) * 4; | ||||||
|  |                     std::memcpy(&gl_buffer[offset], vec4.AsArray(), 4); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             morton_to_gl_fns[static_cast<std::size_t>(pixel_format)](stride, height, &gl_buffer[0], | ||||||
|  |                                                                      addr, load_start, load_end); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); | ||||||
|  | void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) { | ||||||
|  |     u8* const dst_buffer = VideoCore::g_memory->GetPhysicalPointer(addr); | ||||||
|  |     if (dst_buffer == nullptr) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     ASSERT(gl_buffer.size() == width * height * GetBytesPerPixel(pixel_format)); | ||||||
|  | 
 | ||||||
|  |     // TODO: Should probably be done in ::Memory:: and check for other regions too
 | ||||||
|  |     // same as loadglbuffer()
 | ||||||
|  |     if (flush_start < Memory::VRAM_VADDR_END && flush_end > Memory::VRAM_VADDR_END) | ||||||
|  |         flush_end = Memory::VRAM_VADDR_END; | ||||||
|  | 
 | ||||||
|  |     if (flush_start < Memory::VRAM_VADDR && flush_end > Memory::VRAM_VADDR) | ||||||
|  |         flush_start = Memory::VRAM_VADDR; | ||||||
|  | 
 | ||||||
|  |     MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); | ||||||
|  | 
 | ||||||
|  |     ASSERT(flush_start >= addr && flush_end <= end); | ||||||
|  |     const u32 start_offset = flush_start - addr; | ||||||
|  |     const u32 end_offset = flush_end - addr; | ||||||
|  | 
 | ||||||
|  |     if (type == SurfaceType::Fill) { | ||||||
|  |         const u32 coarse_start_offset = start_offset - (start_offset % fill_size); | ||||||
|  |         const u32 backup_bytes = start_offset % fill_size; | ||||||
|  |         std::array<u8, 4> backup_data; | ||||||
|  |         if (backup_bytes) | ||||||
|  |             std::memcpy(&backup_data[0], &dst_buffer[coarse_start_offset], backup_bytes); | ||||||
|  | 
 | ||||||
|  |         for (u32 offset = coarse_start_offset; offset < end_offset; offset += fill_size) { | ||||||
|  |             std::memcpy(&dst_buffer[offset], &fill_data[0], | ||||||
|  |                         std::min(fill_size, end_offset - offset)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (backup_bytes) | ||||||
|  |             std::memcpy(&dst_buffer[coarse_start_offset], &backup_data[0], backup_bytes); | ||||||
|  |     } else if (!is_tiled) { | ||||||
|  |         ASSERT(type == SurfaceType::Color); | ||||||
|  |         if (pixel_format == PixelFormat::RGBA8 && GLES) { | ||||||
|  |             for (std::size_t i = start_offset; i < flush_end - addr; i += 4) { | ||||||
|  |                 dst_buffer[i] = gl_buffer[i + 3]; | ||||||
|  |                 dst_buffer[i + 1] = gl_buffer[i + 2]; | ||||||
|  |                 dst_buffer[i + 2] = gl_buffer[i + 1]; | ||||||
|  |                 dst_buffer[i + 3] = gl_buffer[i]; | ||||||
|  |             } | ||||||
|  |         } else if (pixel_format == PixelFormat::RGB8 && GLES) { | ||||||
|  |             for (std::size_t i = start_offset; i < flush_end - addr; i += 3) { | ||||||
|  |                 dst_buffer[i] = gl_buffer[i + 2]; | ||||||
|  |                 dst_buffer[i + 1] = gl_buffer[i + 1]; | ||||||
|  |                 dst_buffer[i + 2] = gl_buffer[i]; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], | ||||||
|  |                         flush_end - flush_start); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         gl_to_morton_fns[static_cast<std::size_t>(pixel_format)](stride, height, &gl_buffer[0], | ||||||
|  |                                                                  addr, flush_start, flush_end); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CachedSurface::LoadCustomTexture(u64 tex_hash) { | ||||||
|  |     auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache(); | ||||||
|  |     const auto& image_interface = Core::System::GetInstance().GetImageInterface(); | ||||||
|  | 
 | ||||||
|  |     if (custom_tex_cache.IsTextureCached(tex_hash)) { | ||||||
|  |         custom_tex_info = custom_tex_cache.LookupTexture(tex_hash); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!custom_tex_cache.CustomTextureExists(tex_hash)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto& path_info = custom_tex_cache.LookupTexturePathInfo(tex_hash); | ||||||
|  |     if (!image_interface->DecodePNG(custom_tex_info.tex, custom_tex_info.width, | ||||||
|  |                                     custom_tex_info.height, path_info.path)) { | ||||||
|  |         LOG_ERROR(Render_OpenGL, "Failed to load custom texture {}", path_info.path); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const std::bitset<32> width_bits(custom_tex_info.width); | ||||||
|  |     const std::bitset<32> height_bits(custom_tex_info.height); | ||||||
|  |     if (width_bits.count() != 1 || height_bits.count() != 1) { | ||||||
|  |         LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Render_OpenGL, "Loaded custom texture from {}", path_info.path); | ||||||
|  |     Common::FlipRGBA8Texture(custom_tex_info.tex, custom_tex_info.width, custom_tex_info.height); | ||||||
|  |     custom_tex_cache.CacheTexture(tex_hash, custom_tex_info.tex, custom_tex_info.width, | ||||||
|  |                                   custom_tex_info.height); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) { | ||||||
|  |     // Make sure the texture size is a power of 2
 | ||||||
|  |     // If not, the surface is actually a framebuffer
 | ||||||
|  |     std::bitset<32> width_bits(width); | ||||||
|  |     std::bitset<32> height_bits(height); | ||||||
|  |     if (width_bits.count() != 1 || height_bits.count() != 1) { | ||||||
|  |         LOG_WARNING(Render_OpenGL, "Not dumping {:016X} because size isn't a power of 2 ({}x{})", | ||||||
|  |                     tex_hash, width, height); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Dump texture to RGBA8 and encode as PNG
 | ||||||
|  |     const auto& image_interface = Core::System::GetInstance().GetImageInterface(); | ||||||
|  |     auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache(); | ||||||
|  |     std::string dump_path = | ||||||
|  |         fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), | ||||||
|  |                     Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id); | ||||||
|  |     if (!FileUtil::CreateFullPath(dump_path)) { | ||||||
|  |         LOG_ERROR(Render, "Unable to create {}", dump_path); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dump_path += fmt::format("tex1_{}x{}_{:016X}_{}.png", width, height, tex_hash, pixel_format); | ||||||
|  |     if (!custom_tex_cache.IsTextureDumped(tex_hash) && !FileUtil::Exists(dump_path)) { | ||||||
|  |         custom_tex_cache.SetTextureDumped(tex_hash); | ||||||
|  | 
 | ||||||
|  |         LOG_INFO(Render_OpenGL, "Dumping texture to {}", dump_path); | ||||||
|  |         std::vector<u8> decoded_texture; | ||||||
|  |         decoded_texture.resize(width * height * 4); | ||||||
|  |         OpenGLState state = OpenGLState::GetCurState(); | ||||||
|  |         GLuint old_texture = state.texture_units[0].texture_2d; | ||||||
|  |         state.Apply(); | ||||||
|  |         /*
 | ||||||
|  |            GetTexImageOES is used even if not using OpenGL ES to work around a small issue that | ||||||
|  |            happens if using custom textures with texture dumping at the same. | ||||||
|  |            Let's say there's 2 textures that are both 32x32 and one of them gets replaced with a | ||||||
|  |            higher quality 256x256 texture. If the 256x256 texture is displayed first and the | ||||||
|  |            32x32 texture gets uploaded to the same underlying OpenGL texture, the 32x32 texture | ||||||
|  |            will appear in the corner of the 256x256 texture. If texture dumping is enabled and | ||||||
|  |            the 32x32 is undumped, Citra will attempt to dump it. Since the underlying OpenGL | ||||||
|  |            texture is still 256x256, Citra crashes because it thinks the texture is only 32x32. | ||||||
|  |            GetTexImageOES conveniently only dumps the specified region, and works on both | ||||||
|  |            desktop and ES. | ||||||
|  |         */ | ||||||
|  |         // if the backend isn't OpenGL ES, this won't be initialized yet
 | ||||||
|  |         if (!owner.texture_downloader_es) { | ||||||
|  |             owner.texture_downloader_es = std::make_unique<TextureDownloaderES>(false); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, | ||||||
|  |                                                  height, width, &decoded_texture[0]); | ||||||
|  |         state.texture_units[0].texture_2d = old_texture; | ||||||
|  |         state.Apply(); | ||||||
|  |         Common::FlipRGBA8Texture(decoded_texture, width, height); | ||||||
|  |         if (!image_interface->EncodePNG(dump_path, decoded_texture, width, height)) | ||||||
|  |             LOG_ERROR(Render_OpenGL, "Failed to save decoded texture"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); | ||||||
|  | void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) { | ||||||
|  |     if (type == SurfaceType::Fill) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MICROPROFILE_SCOPE(OpenGL_TextureUL); | ||||||
|  |     ASSERT(gl_buffer.size() == width * height * GetBytesPerPixel(pixel_format)); | ||||||
|  | 
 | ||||||
|  |     u64 tex_hash = 0; | ||||||
|  | 
 | ||||||
|  |     if (Settings::values.dump_textures || Settings::values.custom_textures) { | ||||||
|  |         tex_hash = Common::ComputeHash64(gl_buffer.data(), gl_buffer.size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (Settings::values.custom_textures) { | ||||||
|  |         is_custom = LoadCustomTexture(tex_hash); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Load data from memory to the surface
 | ||||||
|  |     GLint x0 = static_cast<GLint>(rect.left); | ||||||
|  |     GLint y0 = static_cast<GLint>(rect.bottom); | ||||||
|  |     std::size_t buffer_offset = (y0 * stride + x0) * GetBytesPerPixel(pixel_format); | ||||||
|  | 
 | ||||||
|  |     const FormatTuple& tuple = GetFormatTuple(pixel_format); | ||||||
|  |     GLuint target_tex = texture.handle; | ||||||
|  | 
 | ||||||
|  |     // If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
 | ||||||
|  |     // surface
 | ||||||
|  |     OGLTexture unscaled_tex; | ||||||
|  |     if (res_scale != 1) { | ||||||
|  |         x0 = 0; | ||||||
|  |         y0 = 0; | ||||||
|  | 
 | ||||||
|  |         if (is_custom) { | ||||||
|  |             const auto& tuple = GetFormatTuple(PixelFormat::RGBA8); | ||||||
|  |             unscaled_tex = owner.AllocateSurfaceTexture(tuple, custom_tex_info.width, | ||||||
|  |                                                         custom_tex_info.height); | ||||||
|  |         } else { | ||||||
|  |             unscaled_tex = owner.AllocateSurfaceTexture(tuple, rect.GetWidth(), rect.GetHeight()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         target_tex = unscaled_tex.handle; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     OpenGLState cur_state = OpenGLState::GetCurState(); | ||||||
|  | 
 | ||||||
|  |     GLuint old_tex = cur_state.texture_units[0].texture_2d; | ||||||
|  |     cur_state.texture_units[0].texture_2d = target_tex; | ||||||
|  |     cur_state.Apply(); | ||||||
|  | 
 | ||||||
|  |     // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
 | ||||||
|  |     ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0); | ||||||
|  |     if (is_custom) { | ||||||
|  |         if (res_scale == 1) { | ||||||
|  |             texture = owner.AllocateSurfaceTexture(GetFormatTuple(PixelFormat::RGBA8), | ||||||
|  |                                                    custom_tex_info.width, custom_tex_info.height); | ||||||
|  |             cur_state.texture_units[0].texture_2d = texture.handle; | ||||||
|  |             cur_state.Apply(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Always going to be using rgba8
 | ||||||
|  |         glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(custom_tex_info.width)); | ||||||
|  | 
 | ||||||
|  |         glActiveTexture(GL_TEXTURE0); | ||||||
|  |         glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, custom_tex_info.width, custom_tex_info.height, | ||||||
|  |                         GL_RGBA, GL_UNSIGNED_BYTE, custom_tex_info.tex.data()); | ||||||
|  |     } else { | ||||||
|  |         glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride)); | ||||||
|  | 
 | ||||||
|  |         glActiveTexture(GL_TEXTURE0); | ||||||
|  |         glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), | ||||||
|  |                         static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, | ||||||
|  |                         &gl_buffer[buffer_offset]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||||
|  |     if (Settings::values.dump_textures && !is_custom) { | ||||||
|  |         DumpTexture(target_tex, tex_hash); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cur_state.texture_units[0].texture_2d = old_tex; | ||||||
|  |     cur_state.Apply(); | ||||||
|  | 
 | ||||||
|  |     if (res_scale != 1) { | ||||||
|  |         auto scaled_rect = rect; | ||||||
|  |         scaled_rect.left *= res_scale; | ||||||
|  |         scaled_rect.top *= res_scale; | ||||||
|  |         scaled_rect.right *= res_scale; | ||||||
|  |         scaled_rect.bottom *= res_scale; | ||||||
|  | 
 | ||||||
|  |         const u32 width = is_custom ? custom_tex_info.width : rect.GetWidth(); | ||||||
|  |         const u32 height = is_custom ? custom_tex_info.height : rect.GetHeight(); | ||||||
|  |         const Common::Rectangle<u32> from_rect{0, height, width, 0}; | ||||||
|  | 
 | ||||||
|  |         if (!owner.texture_filterer->Filter(unscaled_tex, from_rect, texture, scaled_rect, type)) { | ||||||
|  |             const Aspect aspect = ToAspect(type); | ||||||
|  |             runtime.BlitTextures(unscaled_tex, {aspect, from_rect}, | ||||||
|  |                                  texture, {aspect, scaled_rect}); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     InvalidateAllWatcher(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64)); | ||||||
|  | void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) { | ||||||
|  |     if (type == SurfaceType::Fill) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MICROPROFILE_SCOPE(OpenGL_TextureDL); | ||||||
|  | 
 | ||||||
|  |     if (gl_buffer.empty()) { | ||||||
|  |         gl_buffer.resize(width * height * GetBytesPerPixel(pixel_format)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     OpenGLState state = OpenGLState::GetCurState(); | ||||||
|  |     OpenGLState prev_state = state; | ||||||
|  |     SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
|  | 
 | ||||||
|  |     const FormatTuple& tuple = GetFormatTuple(pixel_format); | ||||||
|  | 
 | ||||||
|  |     // Ensure no bad interactions with GL_PACK_ALIGNMENT
 | ||||||
|  |     ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0); | ||||||
|  |     glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride)); | ||||||
|  |     const std::size_t buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format); | ||||||
|  | 
 | ||||||
|  |     // If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
 | ||||||
|  |     const Aspect aspect = ToAspect(type); | ||||||
|  |     if (res_scale != 1) { | ||||||
|  |         auto scaled_rect = rect; | ||||||
|  |         scaled_rect.left *= res_scale; | ||||||
|  |         scaled_rect.top *= res_scale; | ||||||
|  |         scaled_rect.right *= res_scale; | ||||||
|  |         scaled_rect.bottom *= res_scale; | ||||||
|  | 
 | ||||||
|  |         const Common::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0}; | ||||||
|  |         auto unscaled_tex = owner.AllocateSurfaceTexture(tuple, rect.GetWidth(), | ||||||
|  |                                                          rect.GetHeight()); | ||||||
|  |         // Blit scaled texture to the unscaled one
 | ||||||
|  |         runtime.BlitTextures(texture, {aspect, scaled_rect}, | ||||||
|  |                              unscaled_tex, {aspect, unscaled_tex_rect}); | ||||||
|  | 
 | ||||||
|  |         state.texture_units[0].texture_2d = unscaled_tex.handle; | ||||||
|  |         state.Apply(); | ||||||
|  | 
 | ||||||
|  |         glActiveTexture(GL_TEXTURE0); | ||||||
|  |         if (GLES) { | ||||||
|  |             owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, | ||||||
|  |                                                      rect.GetHeight(), rect.GetWidth(), | ||||||
|  |                                                      &gl_buffer[buffer_offset]); | ||||||
|  |         } else { | ||||||
|  |             glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         runtime.ReadTexture(texture, {aspect, rect}, tuple, gl_buffer.data()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     glPixelStorei(GL_PACK_ROW_LENGTH, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CachedSurface::CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const { | ||||||
|  |     if (type == SurfaceType::Fill && IsRegionValid(fill_interval) && | ||||||
|  |         boost::icl::first(fill_interval) >= addr && | ||||||
|  |         boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
 | ||||||
|  |         dest_surface.FromInterval(fill_interval).GetInterval() == | ||||||
|  |             fill_interval) { // make sure interval is a rectangle in dest surface
 | ||||||
|  |         if (fill_size * 8 != dest_surface.GetFormatBpp()) { | ||||||
|  |             // Check if bits repeat for our fill_size
 | ||||||
|  |             const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / 8, 1u); | ||||||
|  |             std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel); | ||||||
|  | 
 | ||||||
|  |             for (u32 i = 0; i < dest_bytes_per_pixel; ++i) | ||||||
|  |                 std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size); | ||||||
|  | 
 | ||||||
|  |             for (u32 i = 0; i < fill_size; ++i) | ||||||
|  |                 if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0], | ||||||
|  |                                 dest_bytes_per_pixel) != 0) | ||||||
|  |                     return false; | ||||||
|  | 
 | ||||||
|  |             if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) | ||||||
|  |                 return false; | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CachedSurface::CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const { | ||||||
|  |     SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval); | ||||||
|  |     ASSERT(subrect_params.GetInterval() == copy_interval); | ||||||
|  |     if (CanSubRect(subrect_params)) | ||||||
|  |         return true; | ||||||
|  | 
 | ||||||
|  |     if (CanFill(dest_surface, copy_interval)) | ||||||
|  |         return true; | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace OpenGL
 | ||||||
							
								
								
									
										136
									
								
								src/video_core/rasterizer_cache/cached_surface.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/video_core/rasterizer_cache/cached_surface.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "core/custom_tex_cache.h" | ||||||
|  | #include "video_core/rasterizer_cache/surface_params.h" | ||||||
|  | #include "video_core/rasterizer_cache/texture_runtime.h" | ||||||
|  | 
 | ||||||
|  | namespace OpenGL { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * A watcher that notifies whether a cached surface has been changed. This is useful for caching | ||||||
|  |  * surface collection objects, including texture cube and mipmap. | ||||||
|  |  */ | ||||||
|  | class SurfaceWatcher { | ||||||
|  |     friend class CachedSurface; | ||||||
|  | public: | ||||||
|  |     explicit SurfaceWatcher(std::weak_ptr<CachedSurface>&& surface) : | ||||||
|  |         surface(std::move(surface)) {} | ||||||
|  | 
 | ||||||
|  |     /// Checks whether the surface has been changed.
 | ||||||
|  |     bool IsValid() const { | ||||||
|  |         return !surface.expired() && valid; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Marks that the content of the referencing surface has been updated to the watcher user.
 | ||||||
|  |     void Validate() { | ||||||
|  |         ASSERT(!surface.expired()); | ||||||
|  |         valid = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets the referencing surface. Returns null if the surface has been destroyed
 | ||||||
|  |     Surface Get() const { | ||||||
|  |         return surface.lock(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::weak_ptr<CachedSurface> surface; | ||||||
|  |     bool valid = false; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class RasterizerCacheOpenGL; | ||||||
|  | 
 | ||||||
|  | class CachedSurface : public SurfaceParams, public std::enable_shared_from_this<CachedSurface> { | ||||||
|  | public: | ||||||
|  |     CachedSurface(RasterizerCacheOpenGL& owner, TextureRuntime& runtime) : | ||||||
|  |         owner(owner), runtime(runtime) {} | ||||||
|  |     ~CachedSurface(); | ||||||
|  | 
 | ||||||
|  |     /// 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); | ||||||
|  | 
 | ||||||
|  |     /// Custom texture loading and dumping
 | ||||||
|  |     bool LoadCustomTexture(u64 tex_hash); | ||||||
|  |     void DumpTexture(GLuint target_tex, u64 tex_hash); | ||||||
|  | 
 | ||||||
|  |     /// Upload/Download data in gl_buffer in/to this surface's texture
 | ||||||
|  |     void UploadGLTexture(Common::Rectangle<u32> rect); | ||||||
|  |     void DownloadGLTexture(const Common::Rectangle<u32>& rect); | ||||||
|  | 
 | ||||||
|  |     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 { | ||||||
|  |         auto interval = GetInterval(); | ||||||
|  |         return *invalid_regions.equal_range(interval).first == interval; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::shared_ptr<SurfaceWatcher> CreateWatcher() { | ||||||
|  |         auto watcher = std::make_shared<SurfaceWatcher>(weak_from_this()); | ||||||
|  |         watchers.push_front(watcher); | ||||||
|  |         return watcher; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void InvalidateAllWatcher() { | ||||||
|  |         for (const auto& watcher : watchers) { | ||||||
|  |             if (auto locked = watcher.lock()) { | ||||||
|  |                 locked->valid = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void UnlinkAllWatcher() { | ||||||
|  |         for (const auto& watcher : watchers) { | ||||||
|  |             if (auto locked = watcher.lock()) { | ||||||
|  |                 locked->valid = false; | ||||||
|  |                 locked->surface.reset(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         watchers.clear(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     bool registered = false; | ||||||
|  |     SurfaceRegions invalid_regions; | ||||||
|  |     std::vector<u8> gl_buffer; | ||||||
|  | 
 | ||||||
|  |     // Number of bytes to read from fill_data
 | ||||||
|  |     u32 fill_size = 0; | ||||||
|  |     std::array<u8, 4> fill_data; | ||||||
|  |     OGLTexture texture; | ||||||
|  | 
 | ||||||
|  |     // level_watchers[i] watches the (i+1)-th level mipmap source surface
 | ||||||
|  |     std::array<std::shared_ptr<SurfaceWatcher>, 7> level_watchers; | ||||||
|  |     u32 max_level = 0; | ||||||
|  | 
 | ||||||
|  |     // Information about custom textures
 | ||||||
|  |     bool is_custom = false; | ||||||
|  |     Core::CustomTexInfo custom_tex_info; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     RasterizerCacheOpenGL& owner; | ||||||
|  |     TextureRuntime& runtime; | ||||||
|  |     std::list<std::weak_ptr<SurfaceWatcher>> watchers; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct CachedTextureCube { | ||||||
|  |     OGLTexture texture; | ||||||
|  |     u16 res_scale = 1; | ||||||
|  |     std::shared_ptr<SurfaceWatcher> px; | ||||||
|  |     std::shared_ptr<SurfaceWatcher> nx; | ||||||
|  |     std::shared_ptr<SurfaceWatcher> py; | ||||||
|  |     std::shared_ptr<SurfaceWatcher> ny; | ||||||
|  |     std::shared_ptr<SurfaceWatcher> pz; | ||||||
|  |     std::shared_ptr<SurfaceWatcher> nz; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace OpenGL
 | ||||||
|  | @ -10,6 +10,8 @@ | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
|  | constexpr u32 PIXEL_FORMAT_COUNT = 18; | ||||||
|  | 
 | ||||||
| enum class PixelFormat : u8 { | enum class PixelFormat : u8 { | ||||||
|     // First 5 formats are shared between textures and color buffers
 |     // First 5 formats are shared between textures and color buffers
 | ||||||
|     RGBA8 = 0, |     RGBA8 = 0, | ||||||
|  | @ -43,7 +45,7 @@ enum class SurfaceType { | ||||||
|     Invalid = 5 |     Invalid = 5 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static constexpr std::string_view PixelFormatAsString(PixelFormat format) { | inline constexpr std::string_view PixelFormatAsString(PixelFormat format) { | ||||||
|     switch (format) { |     switch (format) { | ||||||
|     case PixelFormat::RGBA8: |     case PixelFormat::RGBA8: | ||||||
|         return "RGBA8"; |         return "RGBA8"; | ||||||
|  | @ -84,23 +86,23 @@ static constexpr std::string_view PixelFormatAsString(PixelFormat format) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static constexpr PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) { | inline constexpr PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) { | ||||||
|     const u32 format_index = static_cast<u32>(format); |     const u32 format_index = static_cast<u32>(format); | ||||||
|     return (format_index < 14) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid; |     return (format_index < 14) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static constexpr PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) { | inline constexpr PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) { | ||||||
|     const u32 format_index = static_cast<u32>(format); |     const u32 format_index = static_cast<u32>(format); | ||||||
|     return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid; |     return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) { | inline PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) { | ||||||
|     const u32 format_index = static_cast<u32>(format); |     const u32 format_index = static_cast<u32>(format); | ||||||
|     return (format_index < 4) ? static_cast<PixelFormat>(format_index + 14) |     return (format_index < 4) ? static_cast<PixelFormat>(format_index + 14) | ||||||
|                               : PixelFormat::Invalid; |                               : PixelFormat::Invalid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { | inline constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { | ||||||
|     switch (format) { |     switch (format) { | ||||||
|     // RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
 |     // RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
 | ||||||
|     case GPU::Regs::PixelFormat::RGB565: |     case GPU::Regs::PixelFormat::RGB565: | ||||||
|  | @ -133,7 +135,7 @@ static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { | ||||||
|     return SurfaceType::Invalid; |     return SurfaceType::Invalid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static constexpr bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format) { | inline constexpr bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format) { | ||||||
|     SurfaceType source_type = GetFormatType(source_format); |     SurfaceType source_type = GetFormatType(source_format); | ||||||
|     SurfaceType dest_type = GetFormatType(dest_format); |     SurfaceType dest_type = GetFormatType(dest_format); | ||||||
| 
 | 
 | ||||||
|  | @ -182,7 +184,7 @@ static constexpr u32 GetFormatBpp(PixelFormat format) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static constexpr u32 GetBytesPerPixel(PixelFormat format) { | inline constexpr u32 GetBytesPerPixel(PixelFormat format) { | ||||||
|     // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
 |     // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
 | ||||||
|     if (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) { |     if (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) { | ||||||
|         return 4; |         return 4; | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -4,11 +4,9 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include "common/assert.h" | #include "video_core/rasterizer_cache/cached_surface.h" | ||||||
| #include "core/custom_tex_cache.h" |  | ||||||
| #include "video_core/rasterizer_cache/rasterizer_cache_utils.h" | #include "video_core/rasterizer_cache/rasterizer_cache_utils.h" | ||||||
| #include "video_core/rasterizer_cache/surface_params.h" | #include "video_core/rasterizer_cache/surface_params.h" | ||||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" |  | ||||||
| #include "video_core/texture/texture_decode.h" | #include "video_core/texture/texture_decode.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
|  | @ -19,129 +17,6 @@ enum class ScaleMatch { | ||||||
|     Ignore   // accept every scaled res
 |     Ignore   // accept every scaled res
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * A watcher that notifies whether a cached surface has been changed. This is useful for caching |  | ||||||
|  * surface collection objects, including texture cube and mipmap. |  | ||||||
|  */ |  | ||||||
| struct SurfaceWatcher { |  | ||||||
| public: |  | ||||||
|     explicit SurfaceWatcher(std::weak_ptr<CachedSurface>&& surface) : surface(std::move(surface)) {} |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Checks whether the surface has been changed. |  | ||||||
|      * @return false if the surface content has been changed since last Validate() call or has been |  | ||||||
|      * destroyed; otherwise true |  | ||||||
|      */ |  | ||||||
|     bool IsValid() const { |  | ||||||
|         return !surface.expired() && valid; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Marks that the content of the referencing surface has been updated to the watcher user.
 |  | ||||||
|     void Validate() { |  | ||||||
|         ASSERT(!surface.expired()); |  | ||||||
|         valid = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Gets the referencing surface. Returns null if the surface has been destroyed
 |  | ||||||
|     Surface Get() const { |  | ||||||
|         return surface.lock(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     friend struct CachedSurface; |  | ||||||
|     std::weak_ptr<CachedSurface> surface; |  | ||||||
|     bool valid = false; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class RasterizerCacheOpenGL; |  | ||||||
| 
 |  | ||||||
| struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface> { |  | ||||||
|     CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {} |  | ||||||
|     ~CachedSurface(); |  | ||||||
| 
 |  | ||||||
|     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 { |  | ||||||
|         auto interval = GetInterval(); |  | ||||||
|         return *invalid_regions.equal_range(interval).first == interval; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     bool registered = false; |  | ||||||
|     SurfaceRegions invalid_regions; |  | ||||||
| 
 |  | ||||||
|     u32 fill_size = 0; /// Number of bytes to read from fill_data
 |  | ||||||
|     std::array<u8, 4> fill_data; |  | ||||||
| 
 |  | ||||||
|     OGLTexture texture; |  | ||||||
| 
 |  | ||||||
|     /// max mipmap level that has been attached to the texture
 |  | ||||||
|     u32 max_level = 0; |  | ||||||
|     /// level_watchers[i] watches the (i+1)-th level mipmap source surface
 |  | ||||||
|     std::array<std::shared_ptr<SurfaceWatcher>, 7> level_watchers; |  | ||||||
| 
 |  | ||||||
|     bool is_custom = false; |  | ||||||
|     Core::CustomTexInfo custom_tex_info; |  | ||||||
| 
 |  | ||||||
|     std::vector<u8> gl_buffer; |  | ||||||
| 
 |  | ||||||
|     // 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); |  | ||||||
| 
 |  | ||||||
|     // Custom texture loading and dumping
 |  | ||||||
|     bool LoadCustomTexture(u64 tex_hash); |  | ||||||
|     void DumpTexture(GLuint target_tex, u64 tex_hash); |  | ||||||
| 
 |  | ||||||
|     // Upload/Download data in gl_buffer in/to this surface's texture
 |  | ||||||
|     void UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_handle, GLuint draw_fb_handle); |  | ||||||
|     void DownloadGLTexture(const Common::Rectangle<u32>& rect, GLuint read_fb_handle, |  | ||||||
|                            GLuint draw_fb_handle); |  | ||||||
| 
 |  | ||||||
|     std::shared_ptr<SurfaceWatcher> CreateWatcher() { |  | ||||||
|         auto watcher = std::make_shared<SurfaceWatcher>(weak_from_this()); |  | ||||||
|         watchers.push_front(watcher); |  | ||||||
|         return watcher; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void InvalidateAllWatcher() { |  | ||||||
|         for (const auto& watcher : watchers) { |  | ||||||
|             if (auto locked = watcher.lock()) { |  | ||||||
|                 locked->valid = false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void UnlinkAllWatcher() { |  | ||||||
|         for (const auto& watcher : watchers) { |  | ||||||
|             if (auto locked = watcher.lock()) { |  | ||||||
|                 locked->valid = false; |  | ||||||
|                 locked->surface.reset(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         watchers.clear(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     RasterizerCacheOpenGL& owner; |  | ||||||
|     std::list<std::weak_ptr<SurfaceWatcher>> watchers; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct CachedTextureCube { |  | ||||||
|     OGLTexture texture; |  | ||||||
|     u16 res_scale = 1; |  | ||||||
|     std::shared_ptr<SurfaceWatcher> px; |  | ||||||
|     std::shared_ptr<SurfaceWatcher> nx; |  | ||||||
|     std::shared_ptr<SurfaceWatcher> py; |  | ||||||
|     std::shared_ptr<SurfaceWatcher> ny; |  | ||||||
|     std::shared_ptr<SurfaceWatcher> pz; |  | ||||||
|     std::shared_ptr<SurfaceWatcher> nz; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class TextureDownloaderES; | class TextureDownloaderES; | ||||||
| class TextureFilterer; | class TextureFilterer; | ||||||
| class FormatReinterpreterOpenGL; | class FormatReinterpreterOpenGL; | ||||||
|  | @ -233,14 +108,12 @@ private: | ||||||
|     /// Increase/decrease the number of surface in pages touching the specified region
 |     /// Increase/decrease the number of surface in pages touching the specified region
 | ||||||
|     void UpdatePagesCachedCount(PAddr addr, u32 size, int delta); |     void UpdatePagesCachedCount(PAddr addr, u32 size, int delta); | ||||||
| 
 | 
 | ||||||
|  |     TextureRuntime runtime; | ||||||
|     SurfaceCache surface_cache; |     SurfaceCache surface_cache; | ||||||
|     PageMap cached_pages; |     PageMap cached_pages; | ||||||
|     SurfaceMap dirty_regions; |     SurfaceMap dirty_regions; | ||||||
|     SurfaceSet remove_surfaces; |     SurfaceSet remove_surfaces; | ||||||
| 
 | 
 | ||||||
|     OGLFramebuffer read_framebuffer; |  | ||||||
|     OGLFramebuffer draw_framebuffer; |  | ||||||
| 
 |  | ||||||
|     u16 resolution_scale_factor; |     u16 resolution_scale_factor; | ||||||
| 
 | 
 | ||||||
|     std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache; |     std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache; | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| struct CachedSurface; | class CachedSurface; | ||||||
| using Surface = std::shared_ptr<CachedSurface>; | using Surface = std::shared_ptr<CachedSurface>; | ||||||
| 
 | 
 | ||||||
| // Declare rasterizer interval types
 | // Declare rasterizer interval types
 | ||||||
|  |  | ||||||
|  | @ -4,9 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <set> |  | ||||||
| #include <boost/icl/interval_map.hpp> |  | ||||||
| #include <boost/icl/interval_set.hpp> |  | ||||||
| #include "common/hash.h" | #include "common/hash.h" | ||||||
| #include "video_core/rasterizer_cache/pixel_format.h" | #include "video_core/rasterizer_cache/pixel_format.h" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										196
									
								
								src/video_core/rasterizer_cache/texture_runtime.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/video_core/rasterizer_cache/texture_runtime.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,196 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "common/scope_exit.h" | ||||||
|  | #include "video_core/rasterizer_cache/rasterizer_cache_utils.h" | ||||||
|  | #include "video_core/rasterizer_cache/texture_runtime.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_state.h" | ||||||
|  | 
 | ||||||
|  | namespace OpenGL { | ||||||
|  | 
 | ||||||
|  | GLbitfield ToBufferMask(Aspect aspect) { | ||||||
|  |     switch (aspect) { | ||||||
|  |     case Aspect::Color: | ||||||
|  |         return GL_COLOR_BUFFER_BIT; | ||||||
|  |     case Aspect::Depth: | ||||||
|  |         return GL_DEPTH_BUFFER_BIT; | ||||||
|  |     case Aspect::DepthStencil: | ||||||
|  |         return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TextureRuntime::TextureRuntime() { | ||||||
|  |     read_fbo.Create(); | ||||||
|  |     draw_fbo.Create(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TextureRuntime::ReadTexture(const OGLTexture& tex, Subresource subresource, | ||||||
|  |                                  const FormatTuple& tuple, u8* pixels) { | ||||||
|  | 
 | ||||||
|  |     OpenGLState prev_state = OpenGLState::GetCurState(); | ||||||
|  |     SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
|  | 
 | ||||||
|  |     OpenGLState state; | ||||||
|  |     state.ResetTexture(tex.handle); | ||||||
|  |     state.draw.read_framebuffer = read_fbo.handle; | ||||||
|  |     state.Apply(); | ||||||
|  | 
 | ||||||
|  |     const u32 level = subresource.level; | ||||||
|  |     switch (subresource.aspect) { | ||||||
|  |     case Aspect::Color: | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|  |                                tex.handle, level); | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|  |                                0, 0); | ||||||
|  |         break; | ||||||
|  |     case Aspect::Depth: | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|  |                                tex.handle, level); | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||||
|  |         break; | ||||||
|  |     case Aspect::DepthStencil: | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|  |                                tex.handle, level); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto& rect = subresource.region; | ||||||
|  |     glReadPixels(rect.left, rect.bottom, rect.GetWidth(), rect.GetHeight(), | ||||||
|  |                  tuple.format, tuple.type, pixels); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TextureRuntime::ClearTexture(const OGLTexture& tex, Subresource subresource, | ||||||
|  |                                   ClearValue value) { | ||||||
|  |     OpenGLState prev_state = OpenGLState::GetCurState(); | ||||||
|  |     SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
|  | 
 | ||||||
|  |     // Setup scissor rectangle according to the clear rectangle
 | ||||||
|  |     const auto& clear_rect = subresource.region; | ||||||
|  |     OpenGLState state; | ||||||
|  |     state.scissor.enabled = true; | ||||||
|  |     state.scissor.x = clear_rect.left; | ||||||
|  |     state.scissor.y = clear_rect.bottom; | ||||||
|  |     state.scissor.width = clear_rect.GetWidth(); | ||||||
|  |     state.scissor.height = clear_rect.GetHeight(); | ||||||
|  |     state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|  |     state.Apply(); | ||||||
|  | 
 | ||||||
|  |     const u32 level = subresource.level; | ||||||
|  |     switch (subresource.aspect) { | ||||||
|  |     case Aspect::Color: | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|  |                                tex.handle, level); | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|  |                                0, 0); | ||||||
|  | 
 | ||||||
|  |         state.color_mask.red_enabled = true; | ||||||
|  |         state.color_mask.green_enabled = true; | ||||||
|  |         state.color_mask.blue_enabled = true; | ||||||
|  |         state.color_mask.alpha_enabled = true; | ||||||
|  |         state.Apply(); | ||||||
|  | 
 | ||||||
|  |         glClearBufferfv(GL_COLOR, 0, value.color.AsArray()); | ||||||
|  |         break; | ||||||
|  |     case Aspect::Depth: | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|  |                                tex.handle, level); | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||||
|  | 
 | ||||||
|  |         state.depth.write_mask = GL_TRUE; | ||||||
|  |         state.Apply(); | ||||||
|  | 
 | ||||||
|  |         glClearBufferfv(GL_DEPTH, 0, &value.depth); | ||||||
|  |         break; | ||||||
|  |     case Aspect::DepthStencil: | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|  |                                tex.handle, level); | ||||||
|  | 
 | ||||||
|  |         state.depth.write_mask = GL_TRUE; | ||||||
|  |         state.stencil.write_mask = -1; | ||||||
|  |         state.Apply(); | ||||||
|  | 
 | ||||||
|  |         glClearBufferfi(GL_DEPTH_STENCIL, 0, value.depth, value.stencil); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TextureRuntime::CopyTextures(const OGLTexture& src_tex, Subresource src_subresource, | ||||||
|  |                                   const OGLTexture& dst_tex, Subresource dst_subresource) { | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool TextureRuntime::BlitTextures(const OGLTexture& src_tex, Subresource src_subresource, | ||||||
|  |                                   const OGLTexture& dst_tex, Subresource dst_subresource) { | ||||||
|  |     OpenGLState prev_state = OpenGLState::GetCurState(); | ||||||
|  |     SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
|  | 
 | ||||||
|  |     OpenGLState state; | ||||||
|  |     state.draw.read_framebuffer = read_fbo.handle; | ||||||
|  |     state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|  |     state.Apply(); | ||||||
|  | 
 | ||||||
|  |     auto BindAttachment = [src_level = src_subresource.level, | ||||||
|  |                            dst_level = dst_subresource.level](GLenum target, u32 src_tex, | ||||||
|  |                                                               u32 dst_tex) -> void { | ||||||
|  |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, target, GL_TEXTURE_2D, src_tex, src_level); | ||||||
|  |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, target, GL_TEXTURE_2D, dst_tex, dst_level); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // Sanity check; Can't blit a color texture to a depth buffer
 | ||||||
|  |     ASSERT(src_subresource.aspect == dst_subresource.aspect); | ||||||
|  |     switch (src_subresource.aspect) { | ||||||
|  |     case Aspect::Color: | ||||||
|  |         // Bind only color
 | ||||||
|  |         BindAttachment(GL_COLOR_ATTACHMENT0, src_tex.handle, dst_tex.handle); | ||||||
|  |         BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, 0, 0); | ||||||
|  |         break; | ||||||
|  |     case Aspect::Depth: | ||||||
|  |         // Bind only depth
 | ||||||
|  |         BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0); | ||||||
|  |         BindAttachment(GL_DEPTH_ATTACHMENT, src_tex.handle, dst_tex.handle); | ||||||
|  |         BindAttachment(GL_STENCIL_ATTACHMENT, 0, 0); | ||||||
|  |         break; | ||||||
|  |     case Aspect::DepthStencil: | ||||||
|  |         // Bind to combined depth + stencil
 | ||||||
|  |         BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0); | ||||||
|  |         BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, src_tex.handle, dst_tex.handle); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO (wwylele): use GL_NEAREST for shadow map texture
 | ||||||
|  |     // Note: shadow map is treated as RGBA8 format in PICA, as well as in the rasterizer cache, but
 | ||||||
|  |     // doing linear intepolation componentwise would cause incorrect value. However, for a
 | ||||||
|  |     // well-programmed game this code path should be rarely executed for shadow map with
 | ||||||
|  |     // inconsistent scale.
 | ||||||
|  |     const GLenum filter = src_subresource.aspect == Aspect::Color ? GL_LINEAR : GL_NEAREST; | ||||||
|  |     const auto& src_rect = src_subresource.region; | ||||||
|  |     const auto& dst_rect = dst_subresource.region; | ||||||
|  |     glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, | ||||||
|  |                       dst_rect.left, dst_rect.bottom, dst_rect.right, dst_rect.top, | ||||||
|  |                       ToBufferMask(src_subresource.aspect), filter); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TextureRuntime::GenerateMipmaps(const OGLTexture& tex, u32 max_level) { | ||||||
|  |     OpenGLState prev_state = OpenGLState::GetCurState(); | ||||||
|  |     SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
|  | 
 | ||||||
|  |     OpenGLState state; | ||||||
|  |     state.texture_units[0].texture_2d = tex.handle; | ||||||
|  |     state.Apply(); | ||||||
|  | 
 | ||||||
|  |     glActiveTexture(GL_TEXTURE0); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level); | ||||||
|  | 
 | ||||||
|  |     glGenerateMipmap(GL_TEXTURE_2D); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace OpenGL
 | ||||||
							
								
								
									
										73
									
								
								src/video_core/rasterizer_cache/texture_runtime.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/video_core/rasterizer_cache/texture_runtime.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "common/math_util.h" | ||||||
|  | #include "common/vector_math.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
|  | 
 | ||||||
|  | namespace OpenGL { | ||||||
|  | 
 | ||||||
|  | // Describes the type of data a texture holds
 | ||||||
|  | enum class Aspect { | ||||||
|  |     Color = 0, | ||||||
|  |     Depth = 1, | ||||||
|  |     DepthStencil = 2 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // A union for both color and depth/stencil clear values
 | ||||||
|  | union ClearValue { | ||||||
|  |     Common::Vec4f color; | ||||||
|  |     struct { | ||||||
|  |         float depth; | ||||||
|  |         u8 stencil; | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct Subresource { | ||||||
|  |     Subresource(Aspect aspect, Common::Rectangle<u32> region, u32 level = 0, u32 layer = 0) : | ||||||
|  |         aspect(aspect), region(region), level(level), layer(layer) {} | ||||||
|  | 
 | ||||||
|  |     Aspect aspect; | ||||||
|  |     Common::Rectangle<u32> region; | ||||||
|  |     u32 level = 0; | ||||||
|  |     u32 layer = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct FormatTuple; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Provides texture manipulation functions to the rasterizer cache | ||||||
|  |  * Separating this into a class makes it easier to abstract graphics API code | ||||||
|  |  */ | ||||||
|  | class TextureRuntime { | ||||||
|  | public: | ||||||
|  |     TextureRuntime(); | ||||||
|  |     ~TextureRuntime() = default; | ||||||
|  | 
 | ||||||
|  |     // Copies the GPU pixel data to the provided pixels buffer
 | ||||||
|  |     void ReadTexture(const OGLTexture& tex, Subresource subresource, | ||||||
|  |                      const FormatTuple& tuple, u8* pixels); | ||||||
|  | 
 | ||||||
|  |     // Fills the rectangle of the texture with the clear value provided
 | ||||||
|  |     bool ClearTexture(const OGLTexture& texture, Subresource subresource, ClearValue value); | ||||||
|  | 
 | ||||||
|  |     // Copies a rectangle of src_tex to another rectange of dst_rect
 | ||||||
|  |     // NOTE: The width and height of the rectangles must be equal
 | ||||||
|  |     bool CopyTextures(const OGLTexture& src_tex, Subresource src_subresource, | ||||||
|  |                       const OGLTexture& dst_tex, Subresource dst_subresource); | ||||||
|  | 
 | ||||||
|  |     // Copies a rectangle of src_tex to another rectange of dst_rect performing
 | ||||||
|  |     // scaling and format conversions
 | ||||||
|  |     bool BlitTextures(const OGLTexture& src_tex, Subresource src_subresource, | ||||||
|  |                       const OGLTexture& dst_tex, Subresource dst_subresource); | ||||||
|  | 
 | ||||||
|  |     // Generates mipmaps for all the available levels of the texture
 | ||||||
|  |     void GenerateMipmaps(const OGLTexture& tex, u32 max_level); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     OGLFramebuffer read_fbo, draw_fbo; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace OpenGL
 | ||||||
|  | @ -1,14 +1,12 @@ | ||||||
| // Copyright 2020 Citra Emulator Project
 | // Copyright 2022 Citra Emulator Project
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/scope_exit.h" | #include "common/scope_exit.h" | ||||||
| #include "video_core/renderer_opengl/gl_format_reinterpreter.h" | #include "video_core/renderer_opengl/gl_format_reinterpreter.h" | ||||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" |  | ||||||
| #include "video_core/renderer_opengl/gl_state.h" | #include "video_core/renderer_opengl/gl_state.h" | ||||||
| #include "video_core/renderer_opengl/gl_vars.h" | #include "video_core/renderer_opengl/gl_vars.h" | ||||||
| #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" |  | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
|  | @ -64,15 +62,18 @@ void main() { | ||||||
|         vao.Create(); |         vao.Create(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle, |     PixelFormat GetSourceFormat() const override { | ||||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, |         return PixelFormat::RGBA4; | ||||||
|                      GLuint draw_fb_handle) override { |     } | ||||||
|  | 
 | ||||||
|  |     void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|  |                      const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override { | ||||||
|         OpenGLState prev_state = OpenGLState::GetCurState(); |         OpenGLState prev_state = OpenGLState::GetCurState(); | ||||||
|         SCOPE_EXIT({ prev_state.Apply(); }); |         SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
| 
 | 
 | ||||||
|         OpenGLState state; |         OpenGLState state; | ||||||
|         state.texture_units[0].texture_2d = src_tex; |         state.texture_units[0].texture_2d = src_tex.handle; | ||||||
|         state.draw.draw_framebuffer = draw_fb_handle; |         state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|         state.draw.shader_program = program.handle; |         state.draw.shader_program = program.handle; | ||||||
|         state.draw.vertex_array = vao.handle; |         state.draw.vertex_array = vao.handle; | ||||||
|         state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), |         state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||||
|  | @ -80,10 +81,10 @@ void main() { | ||||||
|                           static_cast<GLsizei>(dst_rect.GetHeight())}; |                           static_cast<GLsizei>(dst_rect.GetHeight())}; | ||||||
|         state.Apply(); |         state.Apply(); | ||||||
| 
 | 
 | ||||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|                                0); |                                dst_tex.handle, 0); | ||||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|                                0); |                                0, 0); | ||||||
| 
 | 
 | ||||||
|         glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); |         glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); | ||||||
|         glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); |         glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); | ||||||
|  | @ -148,15 +149,18 @@ void main() { | ||||||
| 
 | 
 | ||||||
|     ~PixelBufferD24S8toABGR() {} |     ~PixelBufferD24S8toABGR() {} | ||||||
| 
 | 
 | ||||||
|     void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle, |     PixelFormat GetSourceFormat() const override { | ||||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, |         return PixelFormat::D24S8; | ||||||
|                      GLuint draw_fb_handle) override { |     } | ||||||
|  | 
 | ||||||
|  |     void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|  |                      const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override { | ||||||
|         OpenGLState prev_state = OpenGLState::GetCurState(); |         OpenGLState prev_state = OpenGLState::GetCurState(); | ||||||
|         SCOPE_EXIT({ prev_state.Apply(); }); |         SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
| 
 | 
 | ||||||
|         OpenGLState state; |         OpenGLState state; | ||||||
|         state.draw.read_framebuffer = read_fb_handle; |         state.draw.read_framebuffer = read_fbo.handle; | ||||||
|         state.draw.draw_framebuffer = draw_fb_handle; |         state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|         state.Apply(); |         state.Apply(); | ||||||
| 
 | 
 | ||||||
|         glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle); |         glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle); | ||||||
|  | @ -170,7 +174,8 @@ void main() { | ||||||
| 
 | 
 | ||||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||||||
|         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|                                src_tex, 0); |                                src_tex.handle, 0); | ||||||
|  | 
 | ||||||
|         glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom), |         glReadPixels(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom), | ||||||
|                      static_cast<GLsizei>(src_rect.GetWidth()), |                      static_cast<GLsizei>(src_rect.GetWidth()), | ||||||
|                      static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, |                      static_cast<GLsizei>(src_rect.GetHeight()), GL_DEPTH_STENCIL, | ||||||
|  | @ -200,12 +205,11 @@ void main() { | ||||||
|                     static_cast<GLfloat>(state.viewport.width), |                     static_cast<GLfloat>(state.viewport.width), | ||||||
|                     static_cast<GLfloat>(state.viewport.height)); |                     static_cast<GLfloat>(state.viewport.height)); | ||||||
| 
 | 
 | ||||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|                                0); |                                dst_tex.handle, 0); | ||||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|                                0); |                                0, 0); | ||||||
|         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||||
| 
 |  | ||||||
|         glBindTexture(GL_TEXTURE_BUFFER, 0); |         glBindTexture(GL_TEXTURE_BUFFER, 0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -292,19 +296,22 @@ void main() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle, |     PixelFormat GetSourceFormat() const override { | ||||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, |         return PixelFormat::D24S8; | ||||||
|                      GLuint draw_fb_handle) override { |     } | ||||||
|  | 
 | ||||||
|  |     void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|  |                      const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override { | ||||||
|         OpenGLState prev_state = OpenGLState::GetCurState(); |         OpenGLState prev_state = OpenGLState::GetCurState(); | ||||||
|         SCOPE_EXIT({ prev_state.Apply(); }); |         SCOPE_EXIT({ prev_state.Apply(); }); | ||||||
| 
 | 
 | ||||||
|         OpenGLState state; |         OpenGLState state; | ||||||
|         state.texture_units[0].texture_2d = src_tex; |         state.texture_units[0].texture_2d = src_tex.handle; | ||||||
| 
 | 
 | ||||||
|         if (use_texture_view) { |         if (use_texture_view) { | ||||||
|             temp_tex.Create(); |             temp_tex.Create(); | ||||||
|             glActiveTexture(GL_TEXTURE1); |             glActiveTexture(GL_TEXTURE1); | ||||||
|             glTextureView(temp_tex.handle, GL_TEXTURE_2D, src_tex, GL_DEPTH24_STENCIL8, 0, 1, 0, 1); |             glTextureView(temp_tex.handle, GL_TEXTURE_2D, src_tex.handle, GL_DEPTH24_STENCIL8, 0, 1, 0, 1); | ||||||
|             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||||
|             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||||
|         } else if (src_rect.top > temp_rect.top || src_rect.right > temp_rect.right) { |         } else if (src_rect.top > temp_rect.top || src_rect.right > temp_rect.right) { | ||||||
|  | @ -320,7 +327,7 @@ void main() { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         state.texture_units[1].texture_2d = temp_tex.handle; |         state.texture_units[1].texture_2d = temp_tex.handle; | ||||||
|         state.draw.draw_framebuffer = draw_fb_handle; |         state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|         state.draw.shader_program = program.handle; |         state.draw.shader_program = program.handle; | ||||||
|         state.draw.vertex_array = vao.handle; |         state.draw.vertex_array = vao.handle; | ||||||
|         state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), |         state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||||
|  | @ -330,16 +337,16 @@ void main() { | ||||||
| 
 | 
 | ||||||
|         glActiveTexture(GL_TEXTURE1); |         glActiveTexture(GL_TEXTURE1); | ||||||
|         if (!use_texture_view) { |         if (!use_texture_view) { | ||||||
|             glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, |             glCopyImageSubData(src_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, | ||||||
|                                temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, |                                temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, | ||||||
|                                src_rect.GetWidth(), src_rect.GetHeight(), 1); |                                src_rect.GetWidth(), src_rect.GetHeight(), 1); | ||||||
|         } |         } | ||||||
|         glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); |         glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); | ||||||
| 
 | 
 | ||||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|                                0); |                                dst_tex.handle, 0); | ||||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, |         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, | ||||||
|                                0); |                                0, 0); | ||||||
| 
 | 
 | ||||||
|         glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); |         glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); | ||||||
|         glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); |         glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); | ||||||
|  | @ -363,32 +370,32 @@ private: | ||||||
| FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() { | FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() { | ||||||
|     const std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))}; |     const std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))}; | ||||||
|     const std::string_view version{reinterpret_cast<const char*>(glGetString(GL_VERSION))}; |     const std::string_view version{reinterpret_cast<const char*>(glGetString(GL_VERSION))}; | ||||||
|  | 
 | ||||||
|     // Fallback to PBO path on obsolete intel drivers
 |     // Fallback to PBO path on obsolete intel drivers
 | ||||||
|     // intel`s GL_VERSION string - `3.3.0 - Build 25.20.100.6373`
 |     // intel`s GL_VERSION string - `3.3.0 - Build 25.20.100.6373`
 | ||||||
|     const bool intel_broken_drivers = |     const bool intel_broken_drivers = | ||||||
|         vendor.find("Intel") != vendor.npos && (std::atoi(version.substr(14, 2).data()) < 30); |         vendor.find("Intel") != vendor.npos && (std::atoi(version.substr(14, 2).data()) < 30); | ||||||
| 
 | 
 | ||||||
|     if ((!intel_broken_drivers && GLAD_GL_ARB_stencil_texturing && GLAD_GL_ARB_texture_storage && |     auto Register = [this](PixelFormat dest, std::unique_ptr<FormatReinterpreterBase>&& obj) { | ||||||
|          GLAD_GL_ARB_copy_image) || |         const u32 dst_index = static_cast<u32>(dest); | ||||||
|         GLES) { |         return reinterpreters[dst_index].push_back(std::move(obj)); | ||||||
|         reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8}, |     }; | ||||||
|                                std::make_unique<ShaderD24S8toRGBA8>()); | 
 | ||||||
|  |     if ((!intel_broken_drivers && GLAD_GL_ARB_stencil_texturing && | ||||||
|  |          GLAD_GL_ARB_texture_storage && GLAD_GL_ARB_copy_image) || GLES) { | ||||||
|  |         Register(PixelFormat::RGBA8, std::make_unique<ShaderD24S8toRGBA8>()); | ||||||
|         LOG_INFO(Render_OpenGL, "Using shader for D24S8 to RGBA8 reinterpretation"); |         LOG_INFO(Render_OpenGL, "Using shader for D24S8 to RGBA8 reinterpretation"); | ||||||
|     } else { |     } else { | ||||||
|         reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8}, |         Register(PixelFormat::RGBA8, std::make_unique<PixelBufferD24S8toABGR>()); | ||||||
|                                std::make_unique<PixelBufferD24S8toABGR>()); |         LOG_INFO(Render_OpenGL, "Using PBO for D24S8 to RGBA8 reinterpretation"); | ||||||
|         LOG_INFO(Render_OpenGL, "Using pbo for D24S8 to RGBA8 reinterpretation"); |  | ||||||
|     } |     } | ||||||
|     reinterpreters.emplace(PixelFormatPair{PixelFormat::RGB5A1, PixelFormat::RGBA4}, | 
 | ||||||
|                            std::make_unique<RGBA4toRGB5A1>()); |     Register(PixelFormat::RGB5A1, std::make_unique<RGBA4toRGB5A1>()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FormatReinterpreterOpenGL::~FormatReinterpreterOpenGL() = default; | auto FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) | ||||||
| 
 |     -> const ReinterpreterList& { | ||||||
| std::pair<FormatReinterpreterOpenGL::ReinterpreterMap::iterator, |     return reinterpreters[static_cast<u32>(dst_format)]; | ||||||
|           FormatReinterpreterOpenGL::ReinterpreterMap::iterator> |  | ||||||
| FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) { |  | ||||||
|     return reinterpreters.equal_range(dst_format); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace OpenGL
 | } // namespace OpenGL
 | ||||||
|  |  | ||||||
|  | @ -1,62 +1,46 @@ | ||||||
| // Copyright 2020 Citra Emulator Project
 | // Copyright 2022 Citra Emulator Project
 | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <map> | #include <unordered_map> | ||||||
| #include <type_traits> |  | ||||||
| #include <glad/glad.h> |  | ||||||
| #include "common/common_types.h" |  | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "video_core/rasterizer_cache/pixel_format.h" | #include "video_core/rasterizer_cache/pixel_format.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| class RasterizerCacheOpenGL; | class RasterizerCacheOpenGL; | ||||||
| 
 | 
 | ||||||
| struct PixelFormatPair { |  | ||||||
|     const PixelFormat dst_format, src_format; |  | ||||||
| 
 |  | ||||||
|     struct less { |  | ||||||
|         using is_transparent = void; |  | ||||||
|         constexpr bool operator()(PixelFormatPair lhs, PixelFormatPair rhs) const { |  | ||||||
|             return std::tie(lhs.dst_format, lhs.src_format) < |  | ||||||
|                    std::tie(rhs.dst_format, rhs.src_format); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         constexpr bool operator()(PixelFormat lhs, PixelFormatPair rhs) const { |  | ||||||
|             return lhs < rhs.dst_format; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         constexpr bool operator()(PixelFormatPair lhs, PixelFormat rhs) const { |  | ||||||
|             return lhs.dst_format < rhs; |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class FormatReinterpreterBase { | class FormatReinterpreterBase { | ||||||
| public: | public: | ||||||
|  |     FormatReinterpreterBase() { | ||||||
|  |         read_fbo.Create(); | ||||||
|  |         draw_fbo.Create(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     virtual ~FormatReinterpreterBase() = default; |     virtual ~FormatReinterpreterBase() = default; | ||||||
| 
 | 
 | ||||||
|     virtual void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, |     virtual PixelFormat GetSourceFormat() const = 0; | ||||||
|                              GLuint read_fb_handle, GLuint dst_tex, |     virtual void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                              const Common::Rectangle<u32>& dst_rect, GLuint draw_fb_handle) = 0; |                              const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) = 0; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     OGLFramebuffer read_fbo, draw_fbo; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | using ReinterpreterList = std::vector<std::unique_ptr<FormatReinterpreterBase>>; | ||||||
|  | 
 | ||||||
| class FormatReinterpreterOpenGL : NonCopyable { | class FormatReinterpreterOpenGL : NonCopyable { | ||||||
|     using ReinterpreterMap = |  | ||||||
|         std::map<PixelFormatPair, std::unique_ptr<FormatReinterpreterBase>, PixelFormatPair::less>; |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
|     explicit FormatReinterpreterOpenGL(); |     FormatReinterpreterOpenGL(); | ||||||
|     ~FormatReinterpreterOpenGL(); |     ~FormatReinterpreterOpenGL() = default; | ||||||
| 
 | 
 | ||||||
|     auto GetPossibleReinterpretations(PixelFormat dst_format) -> |     const ReinterpreterList& GetPossibleReinterpretations(PixelFormat dst_format); | ||||||
|     std::pair<ReinterpreterMap::iterator, ReinterpreterMap::iterator>; |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     ReinterpreterMap reinterpreters; |     std::array<ReinterpreterList, PIXEL_FORMAT_COUNT> reinterpreters; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace OpenGL
 | } // namespace OpenGL
 | ||||||
|  |  | ||||||
|  | @ -30,7 +30,6 @@ | ||||||
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | ||||||
| 
 | 
 | ||||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" |  | ||||||
| #include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h" | #include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h" | ||||||
| 
 | 
 | ||||||
| #include "shaders/refine.frag" | #include "shaders/refine.frag" | ||||||
|  | @ -72,9 +71,8 @@ Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterBase(scale_f | ||||||
|     cur_state.Apply(); |     cur_state.Apply(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Anime4kUltrafast::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, | void Anime4kUltrafast::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                               GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, |                               const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||||
|                               GLuint read_fb_handle, GLuint draw_fb_handle) { |  | ||||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); |     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||||
| 
 | 
 | ||||||
|     // These will have handles from the previous texture that was filtered, reset them to avoid
 |     // These will have handles from the previous texture that was filtered, reset them to avoid
 | ||||||
|  | @ -112,7 +110,7 @@ void Anime4kUltrafast::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_ | ||||||
|                       static_cast<GLint>(src_rect.bottom * internal_scale_factor), |                       static_cast<GLint>(src_rect.bottom * internal_scale_factor), | ||||||
|                       static_cast<GLsizei>(src_rect.GetWidth() * internal_scale_factor), |                       static_cast<GLsizei>(src_rect.GetWidth() * internal_scale_factor), | ||||||
|                       static_cast<GLsizei>(src_rect.GetHeight() * internal_scale_factor)}; |                       static_cast<GLsizei>(src_rect.GetHeight() * internal_scale_factor)}; | ||||||
|     state.texture_units[0].texture_2d = src_tex; |     state.texture_units[0].texture_2d = src_tex.handle; | ||||||
|     state.texture_units[1].texture_2d = LUMAD.tex.handle; |     state.texture_units[1].texture_2d = LUMAD.tex.handle; | ||||||
|     state.texture_units[2].texture_2d = XY.tex.handle; |     state.texture_units[2].texture_2d = XY.tex.handle; | ||||||
|     state.draw.draw_framebuffer = XY.fbo.handle; |     state.draw.draw_framebuffer = XY.fbo.handle; | ||||||
|  | @ -131,11 +129,12 @@ void Anime4kUltrafast::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_ | ||||||
|     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), |     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetWidth()), |                       static_cast<GLsizei>(dst_rect.GetWidth()), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetHeight())}; |                       static_cast<GLsizei>(dst_rect.GetHeight())}; | ||||||
|     state.draw.draw_framebuffer = draw_fb_handle; |     state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|     state.draw.shader_program = refine_program.handle; |     state.draw.shader_program = refine_program.handle; | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| 
 | 
 | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|  |                            dst_tex.handle, 0); | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,9 +15,8 @@ public: | ||||||
|     static constexpr std::string_view NAME = "Anime4K Ultrafast"; |     static constexpr std::string_view NAME = "Anime4K Ultrafast"; | ||||||
| 
 | 
 | ||||||
|     explicit Anime4kUltrafast(u16 scale_factor); |     explicit Anime4kUltrafast(u16 scale_factor); | ||||||
|     void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, |     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                 const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||||
|                 GLuint draw_fb_handle) override; |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     static constexpr u8 internal_scale_factor = 2; |     static constexpr u8 internal_scale_factor = 2; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" |  | ||||||
| #include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h" | #include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h" | ||||||
| 
 | 
 | ||||||
| #include "shaders/bicubic.frag" | #include "shaders/bicubic.frag" | ||||||
|  | @ -26,18 +25,18 @@ Bicubic::Bicubic(u16 scale_factor) : TextureFilterBase(scale_factor) { | ||||||
|     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||||
| } // namespace OpenGL
 | } // namespace OpenGL
 | ||||||
| 
 | 
 | ||||||
| void Bicubic::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, | void Bicubic::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                      const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |                      const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||||
|                      GLuint draw_fb_handle) { |  | ||||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); |     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||||
|     state.texture_units[0].texture_2d = src_tex; |     state.texture_units[0].texture_2d = src_tex.handle; | ||||||
|     state.draw.draw_framebuffer = draw_fb_handle; |     state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), |     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetWidth()), |                       static_cast<GLsizei>(dst_rect.GetWidth()), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetHeight())}; |                       static_cast<GLsizei>(dst_rect.GetHeight())}; | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| 
 | 
 | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|  |                            dst_tex.handle, 0); | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,9 +15,8 @@ public: | ||||||
|     static constexpr std::string_view NAME = "Bicubic"; |     static constexpr std::string_view NAME = "Bicubic"; | ||||||
| 
 | 
 | ||||||
|     explicit Bicubic(u16 scale_factor); |     explicit Bicubic(u16 scale_factor); | ||||||
|     void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, |     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                 const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||||
|                 GLuint draw_fb_handle) override; |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     OpenGLState state{}; |     OpenGLState state{}; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" |  | ||||||
| #include "video_core/renderer_opengl/texture_filters/scale_force/scale_force.h" | #include "video_core/renderer_opengl/texture_filters/scale_force/scale_force.h" | ||||||
| 
 | 
 | ||||||
| #include "shaders/scale_force.frag" | #include "shaders/scale_force.frag" | ||||||
|  | @ -26,18 +25,18 @@ ScaleForce::ScaleForce(u16 scale_factor) : TextureFilterBase(scale_factor) { | ||||||
|     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScaleForce::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, | void ScaleForce::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                         const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |                         const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||||
|                         GLuint draw_fb_handle) { |  | ||||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); |     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||||
|     state.texture_units[0].texture_2d = src_tex; |     state.texture_units[0].texture_2d = src_tex.handle; | ||||||
|     state.draw.draw_framebuffer = draw_fb_handle; |     state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), |     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetWidth()), |                       static_cast<GLsizei>(dst_rect.GetWidth()), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetHeight())}; |                       static_cast<GLsizei>(dst_rect.GetHeight())}; | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| 
 | 
 | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|  |                            dst_tex.handle, 0); | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,9 +15,8 @@ public: | ||||||
|     static constexpr std::string_view NAME = "ScaleForce"; |     static constexpr std::string_view NAME = "ScaleForce"; | ||||||
| 
 | 
 | ||||||
|     explicit ScaleForce(u16 scale_factor); |     explicit ScaleForce(u16 scale_factor); | ||||||
|     void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, |     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                 const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||||
|                 GLuint draw_fb_handle) override; |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     OpenGLState state{}; |     OpenGLState state{}; | ||||||
|  |  | ||||||
|  | @ -3,23 +3,31 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | #include <string_view> | ||||||
| #include <glad/glad.h> |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
|  | class TextureRuntime; | ||||||
|  | class OGLTexture; | ||||||
|  | 
 | ||||||
| class TextureFilterBase { | class TextureFilterBase { | ||||||
|     friend class TextureFilterer; |     friend class TextureFilterer; | ||||||
|     virtual void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, |  | ||||||
|                         const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |  | ||||||
|                         GLuint draw_fb_handle) = 0; |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
|     explicit TextureFilterBase(u16 scale_factor) : scale_factor{scale_factor} {}; |     explicit TextureFilterBase(u16 scale_factor) : scale_factor(scale_factor) { | ||||||
|  |         draw_fbo.Create(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     virtual ~TextureFilterBase() = default; |     virtual ~TextureFilterBase() = default; | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|  |     virtual void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|  |                         const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) = 0; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     OGLFramebuffer draw_fbo; | ||||||
|     const u16 scale_factor{}; |     const u16 scale_factor{}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -58,16 +58,16 @@ bool TextureFilterer::IsNull() const { | ||||||
|     return !filter; |     return !filter; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TextureFilterer::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, | bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                              const Common::Rectangle<u32>& dst_rect, |                              const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect, | ||||||
|                              SurfaceType type, GLuint read_fb_handle, |                              SurfaceType type) { | ||||||
|                              GLuint draw_fb_handle) { | 
 | ||||||
|     // depth / stencil texture filtering is not supported for now
 |     // Depth/Stencil texture filtering is not supported for now
 | ||||||
|     if (IsNull() || (type != SurfaceType::Color && type != SurfaceType::Texture)) { |     if (IsNull() || (type != SurfaceType::Color && type != SurfaceType::Texture)) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     filter->Filter(src_tex, src_rect, dst_tex, dst_rect, read_fb_handle, draw_fb_handle); |     filter->Filter(src_tex, src_rect, dst_tex, dst_rect); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -83,6 +83,7 @@ std::vector<std::string_view> TextureFilterer::GetFilterNames() { | ||||||
|             return lhs_is_none && !rhs_is_none; |             return lhs_is_none && !rhs_is_none; | ||||||
|         return lhs < rhs; |         return lhs < rhs; | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,15 +16,19 @@ class TextureFilterer { | ||||||
| public: | public: | ||||||
|     static constexpr std::string_view NONE = "none"; |     static constexpr std::string_view NONE = "none"; | ||||||
| 
 | 
 | ||||||
|  | public: | ||||||
|     explicit TextureFilterer(std::string_view filter_name, u16 scale_factor); |     explicit TextureFilterer(std::string_view filter_name, u16 scale_factor); | ||||||
|     // returns true if the filter actually changed
 | 
 | ||||||
|  |     // Returns true if the filter actually changed
 | ||||||
|     bool Reset(std::string_view new_filter_name, u16 new_scale_factor); |     bool Reset(std::string_view new_filter_name, u16 new_scale_factor); | ||||||
|     // returns true if there is no active filter
 | 
 | ||||||
|  |     // Returns true if there is no active filter
 | ||||||
|     bool IsNull() const; |     bool IsNull() const; | ||||||
|     // returns true if the texture was able to be filtered
 | 
 | ||||||
|     bool Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, |     // Returns true if the texture was able to be filtered
 | ||||||
|                 const Common::Rectangle<u32>& dst_rect, SurfaceType type, |     bool Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                 GLuint read_fb_handle, GLuint draw_fb_handle); |                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect, | ||||||
|  |                 SurfaceType type); | ||||||
| 
 | 
 | ||||||
|     static std::vector<std::string_view> GetFilterNames(); |     static std::vector<std::string_view> GetFilterNames(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,7 +40,6 @@ | ||||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||||||
| // THE SOFTWARE.
 | // THE SOFTWARE.
 | ||||||
| 
 | 
 | ||||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" |  | ||||||
| #include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h" | #include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h" | ||||||
| 
 | 
 | ||||||
| #include "shaders/xbrz_freescale.frag" | #include "shaders/xbrz_freescale.frag" | ||||||
|  | @ -48,7 +47,9 @@ | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) { | XbrzFreescale::XbrzFreescale(u16 scale_factor) : | ||||||
|  |     TextureFilterBase(scale_factor) { | ||||||
|  | 
 | ||||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); |     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||||
| 
 | 
 | ||||||
|     program.Create(xbrz_freescale_vert.data(), xbrz_freescale_frag.data()); |     program.Create(xbrz_freescale_vert.data(), xbrz_freescale_frag.data()); | ||||||
|  | @ -62,7 +63,9 @@ XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) | ||||||
|     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||||
|     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||||||
|     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |     glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||||||
|     glUniform1f(glGetUniformLocation(program.handle, "scale"), static_cast<GLfloat>(scale_factor)); | 
 | ||||||
|  |     const GLint scale_loc = glGetUniformLocation(program.handle, "scale"); | ||||||
|  |     glUniform1f(scale_loc, static_cast<GLfloat>(scale_factor)); | ||||||
| 
 | 
 | ||||||
|     cur_state.Apply(); |     cur_state.Apply(); | ||||||
|     state.draw.vertex_array = vao.handle; |     state.draw.vertex_array = vao.handle; | ||||||
|  | @ -70,19 +73,19 @@ XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) | ||||||
|     state.texture_units[0].sampler = src_sampler.handle; |     state.texture_units[0].sampler = src_sampler.handle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void XbrzFreescale::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, | void XbrzFreescale::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                            const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |                            const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||||
|                            GLuint draw_fb_handle) { |  | ||||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); |     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||||
| 
 | 
 | ||||||
|     state.texture_units[0].texture_2d = src_tex; |     state.texture_units[0].texture_2d = src_tex.handle; | ||||||
|     state.draw.draw_framebuffer = draw_fb_handle; |     state.draw.draw_framebuffer = draw_fbo.handle; | ||||||
|     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), |     state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetWidth()), |                       static_cast<GLsizei>(dst_rect.GetWidth()), | ||||||
|                       static_cast<GLsizei>(dst_rect.GetHeight())}; |                       static_cast<GLsizei>(dst_rect.GetHeight())}; | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| 
 | 
 | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|  |                            dst_tex.handle, 0); | ||||||
|     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |     glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | ||||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 |  | ||||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
| #include "video_core/renderer_opengl/gl_state.h" | #include "video_core/renderer_opengl/gl_state.h" | ||||||
| #include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" | #include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" | ||||||
|  | @ -15,9 +14,8 @@ public: | ||||||
|     static constexpr std::string_view NAME = "xBRZ freescale"; |     static constexpr std::string_view NAME = "xBRZ freescale"; | ||||||
| 
 | 
 | ||||||
|     explicit XbrzFreescale(u16 scale_factor); |     explicit XbrzFreescale(u16 scale_factor); | ||||||
|     void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, |     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||||
|                 const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle, |                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||||
|                 GLuint draw_fb_handle) override; |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     OpenGLState state{}; |     OpenGLState state{}; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue