diff --git a/src/common/math_util.h b/src/common/math_util.h index 93add243d..d97d24330 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -22,6 +22,7 @@ struct Rectangle { : left(left), top(top), right(right), bottom(bottom) {} constexpr auto operator<=>(const Rectangle&) const = default; + constexpr void operator*=(const T value) { left *= value; top *= value; @@ -29,6 +30,9 @@ struct Rectangle { bottom *= value; } + [[nodiscard]] constexpr Rectangle operator*(const T value) const { + return Rectangle{left * value, top * value, right * value, bottom * value}; + } [[nodiscard]] constexpr T GetWidth() const { return std::abs(static_cast>(right - left)); } diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index 35126640c..cae7398ed 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #pragma once +#include #include #include #include @@ -46,11 +47,11 @@ template class RasterizerCache : NonCopyable { public: using TextureRuntime = typename T::Runtime; - using CachedSurface = typename T::Surface; + using Surface = std::shared_ptr; + using Watcher = SurfaceWatcher; +private: /// Declare rasterizer interval types - using Surface = std::shared_ptr; - using Watcher = SurfaceWatcher; using SurfaceSet = std::set; using SurfaceMap = boost::icl::interval_map::BlitSurfaces(const Surface& src_surface, Common::Rectan dst_surface->InvalidateAllWatcher(); const TextureBlit texture_blit = { - .surface_type = src_surface->type, .src_level = 0, .dst_level = 0, .src_layer = 0, @@ -277,7 +280,7 @@ bool RasterizerCache::BlitSurfaces(const Surface& src_surface, Common::Rectan .dst_rect = dst_rect }; - return runtime.BlitTextures(src_surface->texture, dst_surface->texture, texture_blit); + return runtime.BlitTextures(*src_surface, *dst_surface, texture_blit); } return false; @@ -305,21 +308,17 @@ void RasterizerCache::CopySurface(const Surface& src_surface, const Surface& const ClearValue clear_value = MakeClearValue(dst_surface->type, dst_surface->pixel_format, fill_buffer.data()); - const TextureClear clear_rect = { - .surface_type = dst_surface->type, - .texture_format = dst_surface->pixel_format, .texture_level = 0, .texture_rect = dst_surface->GetScaledSubRect(subrect_params) }; - runtime.ClearTexture(dst_surface->texture, clear_rect, clear_value); + runtime.ClearTexture(*dst_surface, clear_rect, clear_value); return; } if (src_surface->CanSubRect(subrect_params)) { const TextureBlit texture_blit = { - .surface_type = src_surface->type, .src_level = 0, .dst_level = 0, .src_layer = 0, @@ -328,7 +327,7 @@ void RasterizerCache::CopySurface(const Surface& src_surface, const Surface& .dst_rect = dst_surface->GetScaledSubRect(subrect_params) }; - runtime.BlitTextures(src_surface->texture, dst_surface->texture, texture_blit); + runtime.BlitTextures(*src_surface, *dst_surface, texture_blit); return; } @@ -551,7 +550,6 @@ auto RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& inf if (/*texture_filterer->IsNull()*/true) { const TextureBlit texture_blit = { - .surface_type = surface->type, .src_level = 0, .dst_level = level, .src_layer = 0, @@ -560,7 +558,7 @@ auto RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& inf .dst_rect = surface_params.GetScaledRect() }; - runtime.BlitTextures(level_surface->texture, surface->texture, texture_blit); + runtime.BlitTextures(*level_surface, *surface, texture_blit); } watcher->Validate(); @@ -574,6 +572,7 @@ auto RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& inf template auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> const Surface& { auto& cube = texture_cube_cache[config]; + cube->is_texture_cube = true; struct Face { Face(std::shared_ptr& watcher, PAddr address) @@ -633,7 +632,6 @@ auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> cons } const TextureBlit texture_blit = { - .surface_type = SurfaceType::Color, .src_level = 0, .dst_level = 0, .src_layer = 0, @@ -642,7 +640,7 @@ auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> cons .dst_rect = Rect2D{0, scaled_size, scaled_size, 0} }; - runtime.BlitTextures(surface->texture, cube->texture, texture_blit); + runtime.BlitTextures(*surface, *cube, texture_blit); face.watcher->Validate(); } } @@ -758,7 +756,7 @@ auto RasterizerCache::GetFillSurface(const GPU::Regs::MemoryFillConfig& confi params.type = SurfaceType::Fill; params.res_scale = std::numeric_limits::max(); - Surface new_surface = std::make_shared(params, runtime); + Surface new_surface = std::make_shared(params, runtime); std::memcpy(&new_surface->fill_data[0], &config.value_32bit, 4); if (config.fill_32bit) { @@ -903,8 +901,8 @@ void RasterizerCache::UploadSurface(const Surface& surface, SurfaceInterval i return; } - const u32 start_offset = load_start - surface->addr; const auto upload_data = source_ptr.GetWriteBytes(load_end - load_start); + const u32 start_offset = load_start - surface->addr; const u32 upload_size = static_cast(upload_data.size()); MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad); @@ -925,7 +923,14 @@ void RasterizerCache::UploadSurface(const Surface& surface, SurfaceInterval i UnswizzleTexture(*surface, start_offset, upload_data, staging.mapped); } - surface->UploadTexture(surface->GetSubRect(info), staging); + const BufferTextureCopy upload = { + .buffer_offset = 0, + .buffer_size = staging.size, + .texture_rect = surface->GetSubRect(info), + .texture_level = 0 + }; + + surface->Upload(upload, staging); } MICROPROFILE_DECLARE(RasterizerCache_SurfaceFlush); @@ -937,38 +942,28 @@ void RasterizerCache::DownloadSurface(const Surface& surface, SurfaceInterval const auto& staging = runtime.FindStaging( surface->width * surface->height * GetBytesPerPixel(surface->pixel_format), false); - if (surface->type != SurfaceType::Fill) { - SurfaceParams params = surface->FromInterval(interval); - surface->DownloadTexture(surface->GetSubRect(params), staging); - } + const SurfaceParams params = surface->FromInterval(interval); + const BufferTextureCopy download = { + .buffer_offset = 0, + .buffer_size = staging.size, + .texture_rect = surface->GetSubRect(params), + .texture_level = 0 + }; + + surface->Download(download, staging); MemoryRef dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start); if (!dest_ptr) [[unlikely]] { return; } - const auto start_offset = flush_start - surface->addr; const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start); - const auto download_size = static_cast(download_dest.size()); + const u32 start_offset = flush_start - surface->addr; + const u32 download_size = static_cast(download_dest.size()); MICROPROFILE_SCOPE(RasterizerCache_SurfaceFlush); - if (surface->type == SurfaceType::Fill) { - const u32 coarse_start_offset = start_offset - (start_offset % surface->fill_size); - const u32 backup_bytes = start_offset % surface->fill_size; - std::array backup_data; - if (backup_bytes) { - std::memcpy(backup_data.data(), &dest_ptr[coarse_start_offset], backup_bytes); - } - - for (u32 offset = coarse_start_offset; offset < download_size; offset += surface->fill_size) { - std::memcpy(&dest_ptr[offset], &surface->fill_data[0], - std::min(surface->fill_size, download_size - offset)); - } - - if (backup_bytes) - std::memcpy(&dest_ptr[coarse_start_offset], &backup_data[0], backup_bytes); - } else if (!surface->is_tiled) { + if (!surface->is_tiled) { ASSERT(surface->type == SurfaceType::Color); const auto download_data = staging.mapped.subspan(start_offset, download_size); @@ -985,16 +980,48 @@ void RasterizerCache::DownloadSurface(const Surface& surface, SurfaceInterval } } +template +void RasterizerCache::DownloadFillSurface(const Surface& surface, SurfaceInterval interval) { + const u32 flush_start = boost::icl::first(interval); + const u32 flush_end = boost::icl::last_next(interval); + ASSERT(flush_start >= surface->addr && flush_end <= surface->end); + + MemoryRef dest_ptr = VideoCore::g_memory->GetPhysicalRef(flush_start); + if (!dest_ptr) [[unlikely]] { + return; + } + + const u32 start_offset = flush_start - surface->addr; + const u32 download_size = std::clamp(flush_end - flush_start, 0u, static_cast(dest_ptr.GetSize())); + const u32 coarse_start_offset = start_offset - (start_offset % surface->fill_size); + const u32 backup_bytes = start_offset % surface->fill_size; + + std::array backup_data; + if (backup_bytes) { + std::memcpy(backup_data.data(), &dest_ptr[coarse_start_offset], backup_bytes); + } + + for (u32 offset = coarse_start_offset; offset < download_size; offset += surface->fill_size) { + std::memcpy(&dest_ptr[offset], &surface->fill_data[0], + std::min(surface->fill_size, download_size - offset)); + } + + if (backup_bytes) { + std::memcpy(&dest_ptr[coarse_start_offset], &backup_data[0], backup_bytes); + } +} + template bool RasterizerCache::NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams& params, SurfaceInterval interval) { - static constexpr std::array all_formats{ + static constexpr std::array all_formats = { PixelFormat::RGBA8, PixelFormat::RGB8, PixelFormat::RGB5A1, PixelFormat::RGB565, PixelFormat::RGBA4, PixelFormat::IA8, PixelFormat::RG8, PixelFormat::I8, PixelFormat::A8, PixelFormat::IA4, PixelFormat::I4, PixelFormat::A4, PixelFormat::ETC1, PixelFormat::ETC1A4, PixelFormat::D16, PixelFormat::D24, PixelFormat::D24S8, }; + bool implemented = true; for (PixelFormat format : all_formats) { if (GetFormatBpp(format) == surface->GetFormatBpp()) { @@ -1017,13 +1044,16 @@ bool RasterizerCache::NoUnimplementedReinterpretations(const Surface& surface template bool RasterizerCache::IntervalHasInvalidPixelFormat(SurfaceParams& params, SurfaceInterval interval) { params.pixel_format = PixelFormat::Invalid; - for (const auto& set : RangeFromInterval(surface_cache, interval)) - for (const auto& surface : set.second) + for (const auto& set : RangeFromInterval(surface_cache, interval)) { + for (const auto& surface : set.second) { if (surface->pixel_format == PixelFormat::Invalid) { LOG_DEBUG(Render_OpenGL, "Surface {:#x} found with invalid pixel format", surface->addr); return true; } + } + } + return false; } @@ -1115,7 +1145,12 @@ void RasterizerCache::FlushRegion(PAddr addr, u32 size, Surface flush_surface // Sanity check, this surface is the last one that marked this region dirty ASSERT(surface->IsRegionValid(interval)); - DownloadSurface(surface, interval); + if (surface->type == SurfaceType::Fill) { + DownloadFillSurface(surface, interval); + } else { + DownloadSurface(surface, interval); + } + flushed_intervals += interval; } @@ -1132,11 +1167,11 @@ template void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner) { std::lock_guard lock{mutex}; - if (size == 0) + if (size == 0) [[unlikely]] { return; + } - const SurfaceInterval invalid_interval(addr, addr + size); - + const SurfaceInterval invalid_interval{addr, addr + size}; if (region_owner != nullptr) { ASSERT(region_owner->type != SurfaceType::Texture); ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end); @@ -1195,7 +1230,7 @@ void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, const Surface& r template auto RasterizerCache::CreateSurface(SurfaceParams& params) -> Surface { - Surface surface = std::make_shared(params, runtime); + Surface surface = std::make_shared(params, runtime); surface->invalid_regions.insert(surface->GetInterval()); // Allocate surface texture diff --git a/src/video_core/rasterizer_cache/types.h b/src/video_core/rasterizer_cache/types.h index 6938b4c71..6b324d930 100644 --- a/src/video_core/rasterizer_cache/types.h +++ b/src/video_core/rasterizer_cache/types.h @@ -3,9 +3,9 @@ // Refer to the license.txt file included. #pragma once +#include "common/common_types.h" #include "common/math_util.h" #include "common/vector_math.h" -#include "video_core/rasterizer_cache/pixel_format.h" namespace VideoCore { @@ -34,14 +34,11 @@ union ClearValue { }; struct TextureClear { - SurfaceType surface_type; - PixelFormat texture_format; u32 texture_level; Rect2D texture_rect; }; struct TextureCopy { - SurfaceType surface_type; u32 src_level; u32 dst_level; Offset src_offset; @@ -50,7 +47,6 @@ struct TextureCopy { }; struct TextureBlit { - SurfaceType surface_type; u32 src_level; u32 dst_level; u32 src_layer; @@ -62,9 +58,6 @@ struct TextureBlit { struct BufferTextureCopy { u32 buffer_offset; u32 buffer_size; - u32 buffer_row_length; - u32 buffer_height; - SurfaceType surface_type; Rect2D texture_rect; u32 texture_level; }; diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index d21a0bd14..c5d7d1502 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -52,7 +52,10 @@ GLbitfield MakeBufferMask(VideoCore::SurfaceType type) { return GL_COLOR_BUFFER_BIT; } -TextureRuntime::TextureRuntime(Driver& driver) : driver(driver) { +TextureRuntime::TextureRuntime(Driver& driver) + : driver{driver}, downloader_es{false}, + filterer{Settings::values.texture_filter_name, VideoCore::GetResolutionScaleFactor()} { + read_fbo.Create(); draw_fbo.Create(); } @@ -123,10 +126,10 @@ const FormatTuple& TextureRuntime::GetFormatTuple(VideoCore::PixelFormat pixel_f OGLTexture TextureRuntime::Allocate2D(u32 width, u32 height, VideoCore::PixelFormat format) { const auto& tuple = GetFormatTuple(format); - auto recycled_tex = texture2d_recycler.find({format, width, height}); - if (recycled_tex != texture2d_recycler.end()) { + auto recycled_tex = texture_recycler.find({format, width, height}); + if (recycled_tex != texture_recycler.end()) { OGLTexture texture = std::move(recycled_tex->second); - texture2d_recycler.erase(recycled_tex); + texture_recycler.erase(recycled_tex); return texture; } @@ -151,48 +154,7 @@ OGLTexture TextureRuntime::AllocateCubeMap(u32 width, VideoCore::PixelFormat for return texture; } -void TextureRuntime::ReadTexture(OGLTexture& texture, const VideoCore::BufferTextureCopy& copy, - VideoCore::PixelFormat format) { - - OpenGLState prev_state = OpenGLState::GetCurState(); - SCOPE_EXIT({ prev_state.Apply(); }); - - OpenGLState state{}; - state.ResetTexture(texture.handle); - state.draw.read_framebuffer = read_fbo.handle; - state.Apply(); - - switch (copy.surface_type) { - case VideoCore::SurfaceType::Color: - case VideoCore::SurfaceType::Texture: - case VideoCore::SurfaceType::Fill: - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle, - copy.texture_level); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - break; - case VideoCore::SurfaceType::Depth: - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture.handle, - copy.texture_level); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - break; - case VideoCore::SurfaceType::DepthStencil: - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - texture.handle, copy.texture_level); - break; - default: - UNREACHABLE_MSG("Invalid surface type!"); - } - - const FormatTuple& tuple = GetFormatTuple(format); - glReadPixels(copy.texture_rect.left, copy.texture_rect.bottom, - copy.texture_rect.GetWidth(), copy.texture_rect.GetHeight(), - tuple.format, tuple.type, reinterpret_cast(copy.buffer_offset)); -} - -bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureClear& clear, +bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear, VideoCore::ClearValue value) { OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); @@ -207,11 +169,12 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC state.draw.draw_framebuffer = draw_fbo.handle; state.Apply(); - switch (clear.surface_type) { + GLint handle = surface.texture.handle; + switch (surface.type) { case VideoCore::SurfaceType::Color: case VideoCore::SurfaceType::Texture: case VideoCore::SurfaceType::Fill: - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.handle, + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, handle, clear.texture_level); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); @@ -226,7 +189,7 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC break; case VideoCore::SurfaceType::Depth: glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture.handle, + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, handle, clear.texture_level); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); @@ -238,7 +201,7 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC case VideoCore::SurfaceType::DepthStencil: glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - texture.handle, clear.texture_level); + handle, clear.texture_level); state.depth.write_mask = GL_TRUE; state.stencil.write_mask = -1; @@ -253,11 +216,11 @@ bool TextureRuntime::ClearTexture(OGLTexture& texture, const VideoCore::TextureC return true; } -bool TextureRuntime::CopyTextures(OGLTexture& source, OGLTexture& dest, const VideoCore::TextureCopy& copy) { +bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy) { return true; } -bool TextureRuntime::BlitTextures(OGLTexture& source, OGLTexture& dest, const VideoCore::TextureBlit& blit) { +bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit) { OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); @@ -266,45 +229,20 @@ bool TextureRuntime::BlitTextures(OGLTexture& source, OGLTexture& dest, const Vi state.draw.draw_framebuffer = draw_fbo.handle; state.Apply(); - auto BindAttachment = [&blit, &source, &dest](GLenum attachment, u32 src_tex, u32 dst_tex) -> void { - const GLenum src_target = source.target == GL_TEXTURE_CUBE_MAP ? - GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer : source.target; - const GLenum dst_target = dest.target == GL_TEXTURE_CUBE_MAP ? - GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.dst_layer : dest.target; + const GLenum src_textarget = source.is_texture_cube ? + GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.src_layer : GL_TEXTURE_2D; + BindFramebuffer(GL_READ_FRAMEBUFFER, blit.src_level, src_textarget, source.type, source.texture); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, src_target, src_tex, blit.src_level); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, dst_target, dst_tex, blit.dst_level); - }; - - switch (blit.surface_type) { - case VideoCore::SurfaceType::Color: - case VideoCore::SurfaceType::Texture: - case VideoCore::SurfaceType::Fill: - // Bind only color - BindAttachment(GL_COLOR_ATTACHMENT0, source.handle, dest.handle); - BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, 0, 0); - break; - case VideoCore::SurfaceType::Depth: - // Bind only depth - BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0); - BindAttachment(GL_DEPTH_ATTACHMENT, source.handle, dest.handle); - BindAttachment(GL_STENCIL_ATTACHMENT, 0, 0); - break; - case VideoCore::SurfaceType::DepthStencil: - // Bind to combined depth + stencil - BindAttachment(GL_COLOR_ATTACHMENT0, 0, 0); - BindAttachment(GL_DEPTH_STENCIL_ATTACHMENT, source.handle, dest.handle); - break; - default: - UNREACHABLE_MSG("Invalid surface type!"); - } + const GLenum dst_textarget = dest.is_texture_cube ? + GL_TEXTURE_CUBE_MAP_POSITIVE_X + blit.dst_layer : GL_TEXTURE_2D; + BindFramebuffer(GL_DRAW_FRAMEBUFFER, blit.dst_level, dst_textarget, dest.type, dest.texture); // 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 GLbitfield buffer_mask = MakeBufferMask(blit.surface_type); + const GLbitfield buffer_mask = MakeBufferMask(source.type); const GLenum filter = buffer_mask == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST; glBlitFramebuffer(blit.src_rect.left, blit.src_rect.bottom, blit.src_rect.right, blit.src_rect.top, blit.dst_rect.left, blit.dst_rect.bottom, blit.dst_rect.right, blit.dst_rect.top, @@ -313,166 +251,165 @@ bool TextureRuntime::BlitTextures(OGLTexture& source, OGLTexture& dest, const Vi return true; } -void TextureRuntime::GenerateMipmaps(OGLTexture& texture, u32 max_level) { +void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) { OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); OpenGLState state{}; - state.texture_units[0].texture_2d = texture.handle; + state.texture_units[0].texture_2d = surface.texture.handle; state.Apply(); glActiveTexture(GL_TEXTURE0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level); - glGenerateMipmap(GL_TEXTURE_2D); } +void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarget, + VideoCore::SurfaceType type, OGLTexture& texture) const { + const GLint framebuffer = target == GL_DRAW_FRAMEBUFFER ? draw_fbo.handle : read_fbo.handle; + glBindFramebuffer(target, framebuffer); + + switch (type) { + case VideoCore::SurfaceType::Color: + case VideoCore::SurfaceType::Texture: + glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, texture.handle, level); + break; + case VideoCore::SurfaceType::Depth: + glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, texture.handle, level); + break; + case VideoCore::SurfaceType::DepthStencil: + glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, texture.handle, level); + break; + default: + UNREACHABLE_MSG("Invalid surface type!"); + } +} + +Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime) + : VideoCore::SurfaceBase{params}, runtime{runtime}, driver{runtime.GetDriver()} { + +} + MICROPROFILE_DEFINE(RasterizerCache_TextureUL, "RasterizerCache", "Texture Upload", MP_RGB(128, 192, 64)); -void CachedSurface::UploadTexture(Common::Rectangle rect, const StagingBuffer& staging) { +void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingBuffer& staging) { MICROPROFILE_SCOPE(RasterizerCache_TextureUL); - const FormatTuple& tuple = runtime.GetFormatTuple(pixel_format); - - // Load data from memory to the surface - GLint x0 = static_cast(rect.left); - GLint y0 = static_cast(rect.bottom); - std::size_t buffer_offset = (y0 * stride + x0) * GetBytesPerPixel(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; - - unscaled_tex = runtime.Allocate2D(rect.GetWidth(), rect.GetHeight(), pixel_format); - target_tex = unscaled_tex.handle; - } + // Ensure no bad interactions with GL_UNPACK_ALIGNMENT + ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0); OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, target_tex); - - // Ensure no bad interactions with GL_UNPACK_ALIGNMENT - ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(stride)); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging.buffer.handle); - glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast(rect.GetWidth()), - static_cast(rect.GetHeight()), tuple.format, tuple.type, - reinterpret_cast(buffer_offset)); + const bool is_scaled = res_scale != 1; + if (is_scaled) { + ScaledUpload(upload); + } else { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture.handle); - // Lock the staging buffer until glTexSubImage completes - staging.Lock(); + const auto& tuple = runtime.GetFormatTuple(pixel_format); + glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level, + upload.texture_rect.left, upload.texture_rect.bottom, + upload.texture_rect.GetWidth(), + upload.texture_rect.GetHeight(), + tuple.format, tuple.type, + reinterpret_cast(upload.buffer_offset)); + } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - 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 from_rect{0, rect.GetHeight(), rect.GetWidth(), 0}; - /*if (!owner.texture_filterer->Filter(unscaled_tex, from_rect, texture, scaled_rect, type)) { - const TextureBlit texture_blit = { - .surface_type = type, - .src_level = 0, - .dst_level = 0, - .src_region = Region2D{ - .start = {0, 0}, - .end = {width, height} - }, - .dst_region = Region2D{ - .start = {rect.left, rect.bottom}, - .end = {rect.right, rect.top} - } - }; - - runtime.BlitTextures(unscaled_tex, texture, texture_blit); - }*/ - } - + // Lock the staging buffer until glTexSubImage completes + staging.Lock(); InvalidateAllWatcher(); } MICROPROFILE_DEFINE(RasterizerCache_TextureDL, "RasterizerCache", "Texture Download", MP_RGB(128, 192, 64)); -void CachedSurface::DownloadTexture(Common::Rectangle rect, const StagingBuffer& staging) { +void Surface::Download(const VideoCore::BufferTextureCopy& download, const StagingBuffer& staging) { MICROPROFILE_SCOPE(RasterizerCache_TextureDL); - const FormatTuple& tuple = runtime.GetFormatTuple(pixel_format); - const u32 buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format); - - OpenGLState state = OpenGLState::GetCurState(); - OpenGLState prev_state = state; - SCOPE_EXIT({ prev_state.Apply(); }); - // Ensure no bad interactions with GL_PACK_ALIGNMENT ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0); + + OpenGLState prev_state = OpenGLState::GetCurState(); + SCOPE_EXIT({ prev_state.Apply(); }); + glPixelStorei(GL_PACK_ROW_LENGTH, static_cast(stride)); glBindBuffer(GL_PIXEL_PACK_BUFFER, staging.buffer.handle); - // If not 1x scale, blit scaled texture to a new 1x texture and use that to flush - 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; - - OGLTexture unscaled_tex = runtime.Allocate2D(rect.GetWidth(), rect.GetHeight(), pixel_format); - - const VideoCore::TextureBlit texture_blit = { - .surface_type = type, - .src_level = 0, - .dst_level = 0, - .src_rect = scaled_rect, - .dst_rect = VideoCore::Rect2D{0, rect.GetHeight(), rect.GetWidth(), 0} - }; - - // Blit scaled texture to the unscaled one - runtime.BlitTextures(texture, unscaled_tex, texture_blit); - - 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(), - reinterpret_cast(buffer_offset)); - } else { - glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, reinterpret_cast(buffer_offset)); - }*/ - glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, reinterpret_cast(buffer_offset)); + const bool is_scaled = res_scale != 1; + if (is_scaled) { + ScaledDownload(download); } else { - const u32 download_size = width * height * GetBytesPerPixel(pixel_format); - const VideoCore::BufferTextureCopy texture_download = { - .buffer_offset = buffer_offset, - .buffer_size = download_size, - .buffer_row_length = stride, - .buffer_height = height, - .surface_type = type, - .texture_rect = rect, - .texture_level = 0 - }; + runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture); - runtime.ReadTexture(texture, texture_download, pixel_format); + const auto& tuple = runtime.GetFormatTuple(pixel_format); + glReadPixels(download.texture_rect.left, download.texture_rect.bottom, + download.texture_rect.GetWidth(), download.texture_rect.GetHeight(), + tuple.format, tuple.type, reinterpret_cast(download.buffer_offset)); } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glPixelStorei(GL_PACK_ROW_LENGTH, 0); } -void CachedSurface::Scale() { +void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download) { + const u32 rect_width = download.texture_rect.GetWidth(); + const u32 rect_height = download.texture_rect.GetHeight(); + // Allocate an unscaled texture that fits the download rectangle to use as a blit destination + OGLTexture unscaled_tex = runtime.Allocate2D(rect_width, rect_height, pixel_format); + runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex); + runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, texture); + + // Blit the scaled rectangle to the unscaled texture + const VideoCore::Rect2D scaled_rect = download.texture_rect * res_scale; + glBlitFramebuffer(scaled_rect.left, scaled_rect.bottom, scaled_rect.right, scaled_rect.top, + 0, 0, rect_width, rect_height, MakeBufferMask(type), GL_LINEAR); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, unscaled_tex.handle); + + const auto& tuple = runtime.GetFormatTuple(pixel_format); + if (driver.IsOpenGLES()) { + const auto& downloader_es = runtime.GetDownloaderES(); + downloader_es.GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, + rect_height, rect_width, + reinterpret_cast(download.buffer_offset)); + } else { + glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, + reinterpret_cast(download.buffer_offset)); + } +} + +void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload) { + const u32 rect_width = upload.texture_rect.GetWidth(); + const u32 rect_height = upload.texture_rect.GetHeight(); + + OGLTexture unscaled_tex = runtime.Allocate2D(rect_width, rect_height, pixel_format); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, unscaled_tex.handle); + + const auto& tuple = runtime.GetFormatTuple(pixel_format); + glTexSubImage2D(GL_TEXTURE_2D, upload.texture_level, 0, 0, rect_width, rect_height, + tuple.format, tuple.type, reinterpret_cast(upload.buffer_offset)); + + const auto scaled_rect = upload.texture_rect * res_scale; + const auto unscaled_rect = VideoCore::Rect2D{0, rect_height, rect_width, 0}; + const auto& filterer = runtime.GetFilterer(); + if (!filterer.Filter(unscaled_tex, unscaled_rect, texture, scaled_rect, type)) { + runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, 0, GL_TEXTURE_2D, type, unscaled_tex); + runtime.BindFramebuffer(GL_DRAW_FRAMEBUFFER, upload.texture_level, GL_TEXTURE_2D, type, texture); + + // If filtering fails, resort to normal blitting + glBlitFramebuffer(0, 0, rect_width, rect_height, + upload.texture_rect.left, upload.texture_rect.bottom, + upload.texture_rect.right, upload.texture_rect.top, + MakeBufferMask(type), GL_LINEAR); + } } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index 846267883..4a704ed74 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -9,6 +9,8 @@ #include "video_core/rasterizer_cache/surface_base.h" #include "video_core/rasterizer_cache/types.h" #include "video_core/renderer_opengl/gl_resource_manager.h" +#include "video_core/renderer_opengl/texture_filters/texture_filterer.h" +#include "video_core/renderer_opengl/texture_downloader_es.h" namespace OpenGL { @@ -50,13 +52,14 @@ struct StagingBuffer { }; class Driver; +class Surface; /** * Provides texture manipulation functions to the rasterizer cache * Separating this into a class makes it easier to abstract graphics API code */ class TextureRuntime { - friend class CachedSurface; + friend class Surface; public: TextureRuntime(Driver& driver); ~TextureRuntime() = default; @@ -73,51 +76,74 @@ public: /// Allocates an OpenGL cube map texture with the specified dimentions and format OGLTexture AllocateCubeMap(u32 width, VideoCore::PixelFormat format); - /// Copies the GPU pixel data to the provided pixels buffer - void ReadTexture(OGLTexture& texture, const VideoCore::BufferTextureCopy& copy, - VideoCore::PixelFormat format); - /// Fills the rectangle of the texture with the clear value provided - bool ClearTexture(OGLTexture& texture, const VideoCore::TextureClear& clear, + bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear, VideoCore::ClearValue value); /// Copies a rectangle of src_tex to another rectange of dst_rect - bool CopyTextures(OGLTexture& source, OGLTexture& dest, const VideoCore::TextureCopy& copy); + bool CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy); /// Blits a rectangle of src_tex to another rectange of dst_rect - bool BlitTextures(OGLTexture& source, OGLTexture& dest, const VideoCore::TextureBlit& blit); + bool BlitTextures(Surface& surface, Surface& dest, const VideoCore::TextureBlit& blit); /// Generates mipmaps for all the available levels of the texture - void GenerateMipmaps(OGLTexture& texture, u32 max_level); + void GenerateMipmaps(Surface& surface, u32 max_level); + +private: + /// Returns the framebuffer used for texture downloads + void BindFramebuffer(GLenum target, GLint level, GLenum textarget, + VideoCore::SurfaceType type, OGLTexture& texture) const; + + /// Returns the OpenGL driver class + const Driver& GetDriver() const { + return driver; + } + + /// Returns the class that handles texture downloads for OpenGL ES + const TextureDownloaderES& GetDownloaderES() const { + return downloader_es; + } + + /// Returns the class that handles texture filtering + const TextureFilterer& GetFilterer() const { + return filterer; + } private: Driver& driver; - OGLFramebuffer read_fbo, draw_fbo; - std::unordered_multimap texture2d_recycler; + TextureDownloaderES downloader_es; + TextureFilterer filterer; // Staging buffers stored in increasing size std::multiset upload_buffers; std::multiset download_buffers; + OGLFramebuffer read_fbo, draw_fbo; + + // Recycled textures to reduce driver allocation overhead + std::unordered_multimap texture_recycler; }; -class CachedSurface : public VideoCore::SurfaceBase { +class Surface : public VideoCore::SurfaceBase { public: - CachedSurface(VideoCore::SurfaceParams& params, TextureRuntime& runtime) - : VideoCore::SurfaceBase{params}, runtime{runtime} {} - ~CachedSurface() override = default; + Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime); + ~Surface() override = default; /// Uploads pixel data in staging to a rectangle region of the surface texture - void UploadTexture(Common::Rectangle rect, const StagingBuffer& staging); + void Upload(const VideoCore::BufferTextureCopy& upload, const StagingBuffer& staging); /// Downloads pixel data to staging from a rectangle region of the surface texture - void DownloadTexture(Common::Rectangle rect, const StagingBuffer& staging); + void Download(const VideoCore::BufferTextureCopy& download, const StagingBuffer& staging); private: - /// Replaces the current texture with a scaled version according to res_scale - void Scale(); + /// Downloads scaled image by downscaling the requested rectangle + void ScaledDownload(const VideoCore::BufferTextureCopy& download); + + /// Uploads pixel data to scaled texture + void ScaledUpload(const VideoCore::BufferTextureCopy& upload); private: TextureRuntime& runtime; + const Driver& driver; public: OGLTexture texture{}; @@ -125,7 +151,7 @@ public: struct Traits { using Runtime = TextureRuntime; - using Surface = CachedSurface; + using Surface = Surface; }; using RasterizerCache = VideoCore::RasterizerCache; diff --git a/src/video_core/renderer_opengl/texture_downloader_es.cpp b/src/video_core/renderer_opengl/texture_downloader_es.cpp index 82572dc92..1b36d94cd 100644 --- a/src/video_core/renderer_opengl/texture_downloader_es.cpp +++ b/src/video_core/renderer_opengl/texture_downloader_es.cpp @@ -28,9 +28,6 @@ const FormatTuple& GetFormatTuple(VideoCore::PixelFormat format) { return DEPTH_TUPLES_HACK[static_cast(format)]; } -/** - * Self tests for the texture downloader - */ void TextureDownloaderES::Test() { auto cur_state = OpenGLState::GetCurState(); OpenGLState state; @@ -158,12 +155,8 @@ void main(){ cur_state.Apply(); } -/** - * OpenGL ES does not support glReadBuffer for depth/stencil formats - * This gets around it by converting to a Red surface before downloading - */ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, - GLint height, GLint width) { + GLint height, GLint width) const { ASSERT(width <= MAX_SIZE && height <= MAX_SIZE); const OpenGLState cur_state = OpenGLState::GetCurState(); OpenGLState state; @@ -171,7 +164,7 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL state.draw.vertex_array = vao.handle; OGLTexture texture_view; - const ConversionShader* converter; + const ConversionShader* converter = nullptr; switch (type) { case GL_UNSIGNED_SHORT: state.draw.draw_framebuffer = depth16_fbo.handle; @@ -192,6 +185,7 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL default: UNREACHABLE_MSG("Destination type not recognized"); } + state.draw.shader_program = converter->program.handle; state.viewport = {0, 0, width, height}; state.Apply(); @@ -210,15 +204,8 @@ GLuint TextureDownloaderES::ConvertDepthToColor(GLuint level, GLenum& format, GL return state.draw.draw_framebuffer; } -/** - * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the - * texture to a framebuffer. - * Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp - * Depth texture download assumes that the texture's format tuple matches what is found - * OpenGL::depth_format_tuples - */ void TextureDownloaderES::GetTexImage(GLenum target, GLuint level, GLenum format, GLenum type, - GLint height, GLint width, void* pixels) { + GLint height, GLint width, void* pixels) const { OpenGLState state = OpenGLState::GetCurState(); GLuint texture; const GLuint old_read_buffer = state.draw.read_framebuffer; diff --git a/src/video_core/renderer_opengl/texture_downloader_es.h b/src/video_core/renderer_opengl/texture_downloader_es.h index 0ead93155..88616671e 100644 --- a/src/video_core/renderer_opengl/texture_downloader_es.h +++ b/src/video_core/renderer_opengl/texture_downloader_es.h @@ -3,28 +3,40 @@ // Refer to the license.txt file included. #pragma once - #include "common/common_types.h" #include "video_core/renderer_opengl/gl_resource_manager.h" namespace OpenGL { + class OpenGLState; class TextureDownloaderES { + static constexpr u16 MAX_SIZE = 1024; public: TextureDownloaderES(bool enable_depth_stencil); + /** + * OpenGL ES does not support glGetTexImage. Obtain the pixels by attaching the + * texture to a framebuffer. + * Originally from https://github.com/apitrace/apitrace/blob/master/retrace/glstate_images.cpp + * Depth texture download assumes that the texture's format tuple matches what is found + * OpenGL::depth_format_tuples + */ void GetTexImage(GLenum target, GLuint level, GLenum format, const GLenum type, - GLint height, GLint width, void* pixels); + GLint height, GLint width, void* pixels) const; private: - void Test(); + /** + * OpenGL ES does not support glReadBuffer for depth/stencil formats. + * This gets around it by converting to a Red surface before downloading + */ GLuint ConvertDepthToColor(GLuint level, GLenum& format, GLenum& type, - GLint height, GLint width); + GLint height, GLint width) const; + + /// Self tests for the texture downloader + void Test(); private: - static constexpr u16 MAX_SIZE = 1024; - struct ConversionShader { OGLProgram program; GLint lod_location{-1}; @@ -40,4 +52,5 @@ private: ConversionShader d24s8_r32ui_conversion_shader; OGLSampler sampler; }; + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp b/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp index 533ee6543..f299f37a2 100644 --- a/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp +++ b/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp @@ -60,7 +60,7 @@ bool TextureFilterer::IsNull() const { bool TextureFilterer::Filter(const OGLTexture& src_tex, Common::Rectangle src_rect, const OGLTexture& dst_tex, Common::Rectangle dst_rect, - VideoCore::SurfaceType type) { + VideoCore::SurfaceType type) const { // Depth/Stencil texture filtering is not supported for now if (IsNull() || (type != VideoCore::SurfaceType::Color && type != VideoCore::SurfaceType::Texture)) { diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filterer.h b/src/video_core/renderer_opengl/texture_filters/texture_filterer.h index 2b1cfeb2d..140224ca2 100644 --- a/src/video_core/renderer_opengl/texture_filters/texture_filterer.h +++ b/src/video_core/renderer_opengl/texture_filters/texture_filterer.h @@ -28,7 +28,7 @@ public: // Returns true if the texture was able to be filtered bool Filter(const OGLTexture& src_tex, Common::Rectangle src_rect, const OGLTexture& dst_tex, Common::Rectangle dst_rect, - VideoCore::SurfaceType type); + VideoCore::SurfaceType type) const; static std::vector GetFilterNames();