mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40: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 | ||||
|     renderer_base.cpp | ||||
|     renderer_base.h | ||||
|     rasterizer_cache/cached_surface.cpp | ||||
|     rasterizer_cache/cached_surface.h | ||||
|     rasterizer_cache/morton_swizzle.h | ||||
|     rasterizer_cache/pixel_format.h | ||||
|     rasterizer_cache/rasterizer_cache.cpp | ||||
|  | @ -32,6 +34,8 @@ add_library(video_core STATIC | |||
|     rasterizer_cache/rasterizer_cache_utils.h | ||||
|     rasterizer_cache/surface_params.cpp | ||||
|     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.h | ||||
|     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 { | ||||
| 
 | ||||
| constexpr u32 PIXEL_FORMAT_COUNT = 18; | ||||
| 
 | ||||
| enum class PixelFormat : u8 { | ||||
|     // First 5 formats are shared between textures and color buffers
 | ||||
|     RGBA8 = 0, | ||||
|  | @ -43,7 +45,7 @@ enum class SurfaceType { | |||
|     Invalid = 5 | ||||
| }; | ||||
| 
 | ||||
| static constexpr std::string_view PixelFormatAsString(PixelFormat format) { | ||||
| inline constexpr std::string_view PixelFormatAsString(PixelFormat format) { | ||||
|     switch (format) { | ||||
|     case PixelFormat::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); | ||||
|     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); | ||||
|     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); | ||||
|     return (format_index < 4) ? static_cast<PixelFormat>(format_index + 14) | ||||
|                               : PixelFormat::Invalid; | ||||
| } | ||||
| 
 | ||||
| static constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { | ||||
| inline constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { | ||||
|     switch (format) { | ||||
|     // RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
 | ||||
|     case GPU::Regs::PixelFormat::RGB565: | ||||
|  | @ -133,7 +135,7 @@ static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { | |||
|     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 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
 | ||||
|     if (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) { | ||||
|         return 4; | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -4,11 +4,9 @@ | |||
| 
 | ||||
| #pragma once | ||||
| #include <unordered_map> | ||||
| #include "common/assert.h" | ||||
| #include "core/custom_tex_cache.h" | ||||
| #include "video_core/rasterizer_cache/cached_surface.h" | ||||
| #include "video_core/rasterizer_cache/rasterizer_cache_utils.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" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
|  | @ -19,129 +17,6 @@ enum class ScaleMatch { | |||
|     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 TextureFilterer; | ||||
| class FormatReinterpreterOpenGL; | ||||
|  | @ -233,14 +108,12 @@ private: | |||
|     /// Increase/decrease the number of surface in pages touching the specified region
 | ||||
|     void UpdatePagesCachedCount(PAddr addr, u32 size, int delta); | ||||
| 
 | ||||
|     TextureRuntime runtime; | ||||
|     SurfaceCache surface_cache; | ||||
|     PageMap cached_pages; | ||||
|     SurfaceMap dirty_regions; | ||||
|     SurfaceSet remove_surfaces; | ||||
| 
 | ||||
|     OGLFramebuffer read_framebuffer; | ||||
|     OGLFramebuffer draw_framebuffer; | ||||
| 
 | ||||
|     u16 resolution_scale_factor; | ||||
| 
 | ||||
|     std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache; | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ | |||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| struct CachedSurface; | ||||
| class CachedSurface; | ||||
| using Surface = std::shared_ptr<CachedSurface>; | ||||
| 
 | ||||
| // Declare rasterizer interval types
 | ||||
|  |  | |||
|  | @ -4,9 +4,6 @@ | |||
| 
 | ||||
| #pragma once | ||||
| #include <functional> | ||||
| #include <set> | ||||
| #include <boost/icl/interval_map.hpp> | ||||
| #include <boost/icl/interval_set.hpp> | ||||
| #include "common/hash.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
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/scope_exit.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_vars.h" | ||||
| #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
|  | @ -64,15 +62,18 @@ void main() { | |||
|         vao.Create(); | ||||
|     } | ||||
| 
 | ||||
|     void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle, | ||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, | ||||
|                      GLuint draw_fb_handle) override { | ||||
|     PixelFormat GetSourceFormat() const override { | ||||
|         return PixelFormat::RGBA4; | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
|         SCOPE_EXIT({ prev_state.Apply(); }); | ||||
| 
 | ||||
|         OpenGLState state; | ||||
|         state.texture_units[0].texture_2d = src_tex; | ||||
|         state.draw.draw_framebuffer = draw_fb_handle; | ||||
|         state.texture_units[0].texture_2d = src_tex.handle; | ||||
|         state.draw.draw_framebuffer = draw_fbo.handle; | ||||
|         state.draw.shader_program = program.handle; | ||||
|         state.draw.vertex_array = vao.handle; | ||||
|         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())}; | ||||
|         state.Apply(); | ||||
| 
 | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, | ||||
|                                0); | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||||
|                                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); | ||||
| 
 | ||||
|         glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); | ||||
|         glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); | ||||
|  | @ -148,15 +149,18 @@ void main() { | |||
| 
 | ||||
|     ~PixelBufferD24S8toABGR() {} | ||||
| 
 | ||||
|     void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint read_fb_handle, | ||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, | ||||
|                      GLuint draw_fb_handle) override { | ||||
|     PixelFormat GetSourceFormat() const override { | ||||
|         return PixelFormat::D24S8; | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
|         SCOPE_EXIT({ prev_state.Apply(); }); | ||||
| 
 | ||||
|         OpenGLState state; | ||||
|         state.draw.read_framebuffer = read_fb_handle; | ||||
|         state.draw.draw_framebuffer = draw_fb_handle; | ||||
|         state.draw.read_framebuffer = read_fbo.handle; | ||||
|         state.draw.draw_framebuffer = draw_fbo.handle; | ||||
|         state.Apply(); | ||||
| 
 | ||||
|         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_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), | ||||
|                      static_cast<GLsizei>(src_rect.GetWidth()), | ||||
|                      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.height)); | ||||
| 
 | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, | ||||
|                                0); | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||||
|                                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); | ||||
|         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
| 
 | ||||
|         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, | ||||
|                      GLuint dst_tex, const Common::Rectangle<u32>& dst_rect, | ||||
|                      GLuint draw_fb_handle) override { | ||||
|     PixelFormat GetSourceFormat() const override { | ||||
|         return PixelFormat::D24S8; | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
|         SCOPE_EXIT({ prev_state.Apply(); }); | ||||
| 
 | ||||
|         OpenGLState state; | ||||
|         state.texture_units[0].texture_2d = src_tex; | ||||
|         state.texture_units[0].texture_2d = src_tex.handle; | ||||
| 
 | ||||
|         if (use_texture_view) { | ||||
|             temp_tex.Create(); | ||||
|             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_MIN_FILTER, GL_NEAREST); | ||||
|         } 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.draw.draw_framebuffer = draw_fb_handle; | ||||
|         state.draw.draw_framebuffer = draw_fbo.handle; | ||||
|         state.draw.shader_program = program.handle; | ||||
|         state.draw.vertex_array = vao.handle; | ||||
|         state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom), | ||||
|  | @ -330,16 +337,16 @@ void main() { | |||
| 
 | ||||
|         glActiveTexture(GL_TEXTURE1); | ||||
|         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, | ||||
|                                src_rect.GetWidth(), src_rect.GetHeight(), 1); | ||||
|         } | ||||
|         glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); | ||||
| 
 | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, | ||||
|                                0); | ||||
|         glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | ||||
|                                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); | ||||
| 
 | ||||
|         glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); | ||||
|         glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); | ||||
|  | @ -363,32 +370,32 @@ private: | |||
| FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() { | ||||
|     const std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))}; | ||||
|     const std::string_view version{reinterpret_cast<const char*>(glGetString(GL_VERSION))}; | ||||
| 
 | ||||
|     // Fallback to PBO path on obsolete intel drivers
 | ||||
|     // intel`s GL_VERSION string - `3.3.0 - Build 25.20.100.6373`
 | ||||
|     const bool intel_broken_drivers = | ||||
|         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 && | ||||
|          GLAD_GL_ARB_copy_image) || | ||||
|         GLES) { | ||||
|         reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8}, | ||||
|                                std::make_unique<ShaderD24S8toRGBA8>()); | ||||
|     auto Register = [this](PixelFormat dest, std::unique_ptr<FormatReinterpreterBase>&& obj) { | ||||
|         const u32 dst_index = static_cast<u32>(dest); | ||||
|         return reinterpreters[dst_index].push_back(std::move(obj)); | ||||
|     }; | ||||
| 
 | ||||
|     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"); | ||||
|     } else { | ||||
|         reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8}, | ||||
|                                std::make_unique<PixelBufferD24S8toABGR>()); | ||||
|         LOG_INFO(Render_OpenGL, "Using pbo for D24S8 to RGBA8 reinterpretation"); | ||||
|         Register(PixelFormat::RGBA8, std::make_unique<PixelBufferD24S8toABGR>()); | ||||
|         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; | ||||
| 
 | ||||
| std::pair<FormatReinterpreterOpenGL::ReinterpreterMap::iterator, | ||||
|           FormatReinterpreterOpenGL::ReinterpreterMap::iterator> | ||||
| FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) { | ||||
|     return reinterpreters.equal_range(dst_format); | ||||
| auto FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) | ||||
|     -> const ReinterpreterList& { | ||||
|     return reinterpreters[static_cast<u32>(dst_format)]; | ||||
| } | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
|  |  | |||
|  | @ -1,62 +1,46 @@ | |||
| // Copyright 2020 Citra Emulator Project
 | ||||
| // Copyright 2022 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include <type_traits> | ||||
| #include <glad/glad.h> | ||||
| #include "common/common_types.h" | ||||
| #include <unordered_map> | ||||
| #include "common/math_util.h" | ||||
| #include "video_core/rasterizer_cache/pixel_format.h" | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| 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 { | ||||
| public: | ||||
|     FormatReinterpreterBase() { | ||||
|         read_fbo.Create(); | ||||
|         draw_fbo.Create(); | ||||
|     } | ||||
| 
 | ||||
|     virtual ~FormatReinterpreterBase() = default; | ||||
| 
 | ||||
|     virtual void Reinterpret(GLuint src_tex, const Common::Rectangle<u32>& src_rect, | ||||
|                              GLuint read_fb_handle, GLuint dst_tex, | ||||
|                              const Common::Rectangle<u32>& dst_rect, GLuint draw_fb_handle) = 0; | ||||
|     virtual PixelFormat GetSourceFormat() const = 0; | ||||
|     virtual void Reinterpret(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                              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 { | ||||
|     using ReinterpreterMap = | ||||
|         std::map<PixelFormatPair, std::unique_ptr<FormatReinterpreterBase>, PixelFormatPair::less>; | ||||
| 
 | ||||
| public: | ||||
|     explicit FormatReinterpreterOpenGL(); | ||||
|     ~FormatReinterpreterOpenGL(); | ||||
|     FormatReinterpreterOpenGL(); | ||||
|     ~FormatReinterpreterOpenGL() = default; | ||||
| 
 | ||||
|     auto GetPossibleReinterpretations(PixelFormat dst_format) -> | ||||
|     std::pair<ReinterpreterMap::iterator, ReinterpreterMap::iterator>; | ||||
|     const ReinterpreterList& GetPossibleReinterpretations(PixelFormat dst_format); | ||||
| 
 | ||||
| private: | ||||
|     ReinterpreterMap reinterpreters; | ||||
|     std::array<ReinterpreterList, PIXEL_FORMAT_COUNT> reinterpreters; | ||||
| }; | ||||
| 
 | ||||
| } // namespace OpenGL
 | ||||
|  |  | |||
|  | @ -30,7 +30,6 @@ | |||
| // 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.
 | ||||
| 
 | ||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" | ||||
| #include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h" | ||||
| 
 | ||||
| #include "shaders/refine.frag" | ||||
|  | @ -72,9 +71,8 @@ Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterBase(scale_f | |||
|     cur_state.Apply(); | ||||
| } | ||||
| 
 | ||||
| void Anime4kUltrafast::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) { | ||||
| void Anime4kUltrafast::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                               const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
| 
 | ||||
|     // 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<GLsizei>(src_rect.GetWidth() * 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[2].texture_2d = XY.tex.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), | ||||
|                       static_cast<GLsizei>(dst_rect.GetWidth()), | ||||
|                       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.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); | ||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,9 +15,8 @@ public: | |||
|     static constexpr std::string_view NAME = "Anime4K Ultrafast"; | ||||
| 
 | ||||
|     explicit Anime4kUltrafast(u16 scale_factor); | ||||
|     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) override; | ||||
|     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||
| 
 | ||||
| private: | ||||
|     static constexpr u8 internal_scale_factor = 2; | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // 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 "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); | ||||
| } // namespace OpenGL
 | ||||
| 
 | ||||
| void Bicubic::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) { | ||||
| void Bicubic::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                      const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
|     state.texture_units[0].texture_2d = src_tex; | ||||
|     state.draw.draw_framebuffer = draw_fb_handle; | ||||
|     state.texture_units[0].texture_2d = src_tex.handle; | ||||
|     state.draw.draw_framebuffer = draw_fbo.handle; | ||||
|     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.GetHeight())}; | ||||
|     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); | ||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,9 +15,8 @@ public: | |||
|     static constexpr std::string_view NAME = "Bicubic"; | ||||
| 
 | ||||
|     explicit Bicubic(u16 scale_factor); | ||||
|     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) override; | ||||
|     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||
| 
 | ||||
| private: | ||||
|     OpenGLState state{}; | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // 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 "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); | ||||
| } | ||||
| 
 | ||||
| void ScaleForce::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) { | ||||
| void ScaleForce::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                         const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
|     state.texture_units[0].texture_2d = src_tex; | ||||
|     state.draw.draw_framebuffer = draw_fb_handle; | ||||
|     state.texture_units[0].texture_2d = src_tex.handle; | ||||
|     state.draw.draw_framebuffer = draw_fbo.handle; | ||||
|     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.GetHeight())}; | ||||
|     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); | ||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,9 +15,8 @@ public: | |||
|     static constexpr std::string_view NAME = "ScaleForce"; | ||||
| 
 | ||||
|     explicit ScaleForce(u16 scale_factor); | ||||
|     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) override; | ||||
|     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||
| 
 | ||||
| private: | ||||
|     OpenGLState state{}; | ||||
|  |  | |||
|  | @ -3,23 +3,31 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <glad/glad.h> | ||||
| #include <string_view> | ||||
| #include "common/common_types.h" | ||||
| #include "common/math_util.h" | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| class TextureRuntime; | ||||
| class OGLTexture; | ||||
| 
 | ||||
| class TextureFilterBase { | ||||
|     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: | ||||
|     explicit TextureFilterBase(u16 scale_factor) : scale_factor{scale_factor} {}; | ||||
|     explicit TextureFilterBase(u16 scale_factor) : scale_factor(scale_factor) { | ||||
|         draw_fbo.Create(); | ||||
|     }; | ||||
| 
 | ||||
|     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{}; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,16 +58,16 @@ bool TextureFilterer::IsNull() const { | |||
|     return !filter; | ||||
| } | ||||
| 
 | ||||
| bool TextureFilterer::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex, | ||||
|                              const Common::Rectangle<u32>& dst_rect, | ||||
|                              SurfaceType type, GLuint read_fb_handle, | ||||
|                              GLuint draw_fb_handle) { | ||||
|     // depth / stencil texture filtering is not supported for now
 | ||||
| bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                              const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect, | ||||
|                              SurfaceType type) { | ||||
| 
 | ||||
|     // Depth/Stencil texture filtering is not supported for now
 | ||||
|     if (IsNull() || (type != SurfaceType::Color && type != SurfaceType::Texture)) { | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
|  | @ -83,6 +83,7 @@ std::vector<std::string_view> TextureFilterer::GetFilterNames() { | |||
|             return lhs_is_none && !rhs_is_none; | ||||
|         return lhs < rhs; | ||||
|     }); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,15 +16,19 @@ class TextureFilterer { | |||
| public: | ||||
|     static constexpr std::string_view NONE = "none"; | ||||
| 
 | ||||
| public: | ||||
|     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); | ||||
|     // returns true if there is no active filter
 | ||||
| 
 | ||||
|     // Returns true if there is no active filter
 | ||||
|     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, | ||||
|                 const Common::Rectangle<u32>& dst_rect, SurfaceType type, | ||||
|                 GLuint read_fb_handle, GLuint draw_fb_handle); | ||||
| 
 | ||||
|     // Returns true if the texture was able to be filtered
 | ||||
|     bool Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect, | ||||
|                 SurfaceType type); | ||||
| 
 | ||||
|     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
 | ||||
| // THE SOFTWARE.
 | ||||
| 
 | ||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" | ||||
| #include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h" | ||||
| 
 | ||||
| #include "shaders/xbrz_freescale.frag" | ||||
|  | @ -48,7 +47,9 @@ | |||
| 
 | ||||
| namespace OpenGL { | ||||
| 
 | ||||
| XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) { | ||||
| XbrzFreescale::XbrzFreescale(u16 scale_factor) : | ||||
|     TextureFilterBase(scale_factor) { | ||||
| 
 | ||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
| 
 | ||||
|     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_WRAP_S, 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(); | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| void XbrzFreescale::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) { | ||||
| void XbrzFreescale::Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                            const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) { | ||||
|     const OpenGLState cur_state = OpenGLState::GetCurState(); | ||||
| 
 | ||||
|     state.texture_units[0].texture_2d = src_tex; | ||||
|     state.draw.draw_framebuffer = draw_fb_handle; | ||||
|     state.texture_units[0].texture_2d = src_tex.handle; | ||||
|     state.draw.draw_framebuffer = draw_fbo.handle; | ||||
|     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.GetHeight())}; | ||||
|     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); | ||||
|     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
| #include "video_core/renderer_opengl/gl_state.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"; | ||||
| 
 | ||||
|     explicit XbrzFreescale(u16 scale_factor); | ||||
|     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) override; | ||||
|     void Filter(const OGLTexture& src_tex, Common::Rectangle<u32> src_rect, | ||||
|                 const OGLTexture& dst_tex, Common::Rectangle<u32> dst_rect) override; | ||||
| 
 | ||||
| private: | ||||
|     OpenGLState state{}; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue