diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index cae7398ed..0144686e3 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -571,64 +571,62 @@ 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; + auto [it, new_surface] = texture_cube_cache.try_emplace(config); + if (new_surface) { + SurfaceParams cube_params = { + .addr = config.px, + .width = config.width, + .height = config.width, + .stride = config.width, + .texture_type = TextureType::CubeMap, + .pixel_format = PixelFormatFromTextureFormat(config.format), + .type = SurfaceType::Texture + }; - struct Face { - Face(std::shared_ptr& watcher, PAddr address) - : watcher(watcher), address(address) {} - std::shared_ptr& watcher; - PAddr address; - }; + it->second = CreateSurface(cube_params); + } - const std::array faces{{ - {cube->level_watchers[0], config.px}, - {cube->level_watchers[1], config.nx}, - {cube->level_watchers[2], config.py}, - {cube->level_watchers[3], config.ny}, - {cube->level_watchers[4], config.pz}, - {cube->level_watchers[5], config.nz}, - }}; + Surface& cube = it->second; + + // Update surface watchers + auto& watchers = cube->level_watchers; + const std::array addresses = {config.px, config.nx, config.py, + config.ny, config.pz, config.nz}; + + for (std::size_t i = 0; i < addresses.size(); i++) { + auto& watcher = watchers[i]; + if (!watcher || !watcher->Get()) { + Pica::Texture::TextureInfo info = { + .physical_address = addresses[i], + .width = config.width, + .height = config.width, + .format = config.format, + }; - for (const Face& face : faces) { - if (!face.watcher || !face.watcher->Get()) { - Pica::Texture::TextureInfo info; - info.physical_address = face.address; - info.height = info.width = config.width; - info.format = config.format; info.SetDefaultStride(); auto surface = GetTextureSurface(info); if (surface) { - face.watcher = surface->CreateWatcher(); + watcher = surface->CreateWatcher(); } else { - // Can occur when texture address is invalid. We mark the watcher with nullptr - // in this case and the content of the face wouldn't get updated. These are - // usually leftover setup in the texture unit and games are not supposed to draw - // using them. - face.watcher = nullptr; + /** + * Can occur when texture address is invalid. We mark the watcher with nullptr + * in this case and the content of the face wouldn't get updated. These are + * usually leftover setup in the texture unit and games are not supposed to draw + * using them. + */ + watcher = nullptr; } } } - if (cube->texture.handle == 0) { - for (const Face& face : faces) { - if (face.watcher) { - auto surface = face.watcher->Get(); - cube->res_scale = std::max(cube->res_scale, surface->res_scale); - } - } - - const u32 width = cube->res_scale * config.width; - cube->texture = runtime.AllocateCubeMap(width, PixelFormatFromTextureFormat(config.format)); - } - - const u32 scaled_size = cube->res_scale * config.width; - for (std::size_t i = 0; i < faces.size(); i++) { - const Face& face = faces[i]; - if (face.watcher && !face.watcher->IsValid()) { - auto surface = face.watcher->Get(); - if (!surface->invalid_regions.empty()) { - ValidateSurface(surface, surface->addr, surface->size); + // Validate the face surfaces + const u32 scaled_size = cube->GetScaledWidth(); + for (std::size_t i = 0; i < addresses.size(); i++) { + const auto& watcher = watchers[i]; + if (watcher && !watcher->IsValid()) { + auto face = watcher->Get(); + if (!face->invalid_regions.empty()) { + ValidateSurface(face, face->addr, face->size); } const TextureBlit texture_blit = { @@ -636,12 +634,12 @@ auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> cons .dst_level = 0, .src_layer = 0, .dst_layer = static_cast(i), - .src_rect = surface->GetScaledRect(), + .src_rect = face->GetScaledRect(), .dst_rect = Rect2D{0, scaled_size, scaled_size, 0} }; - runtime.BlitTextures(*surface, *cube, texture_blit); - face.watcher->Validate(); + runtime.BlitTextures(*face, *cube, texture_blit); + watcher->Validate(); } } @@ -1233,10 +1231,6 @@ auto RasterizerCache::CreateSurface(SurfaceParams& params) -> Surface { Surface surface = std::make_shared(params, runtime); surface->invalid_regions.insert(surface->GetInterval()); - // Allocate surface texture - surface->texture = - runtime.Allocate2D(surface->GetScaledWidth(), surface->GetScaledHeight(), params.pixel_format); - return surface; } diff --git a/src/video_core/rasterizer_cache/surface_base.h b/src/video_core/rasterizer_cache/surface_base.h index 27336354c..ad1dd4d29 100644 --- a/src/video_core/rasterizer_cache/surface_base.h +++ b/src/video_core/rasterizer_cache/surface_base.h @@ -7,6 +7,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "video_core/rasterizer_cache/surface_params.h" +#include "video_core/rasterizer_cache/utils.h" namespace VideoCore { @@ -80,7 +81,6 @@ public: public: bool registered = false; - bool is_texture_cube = false; SurfaceRegions invalid_regions; std::array, 7> level_watchers; u32 max_level = 0; diff --git a/src/video_core/rasterizer_cache/surface_params.h b/src/video_core/rasterizer_cache/surface_params.h index 8d5f75869..4276f3cf4 100644 --- a/src/video_core/rasterizer_cache/surface_params.h +++ b/src/video_core/rasterizer_cache/surface_params.h @@ -15,6 +15,11 @@ namespace VideoCore { using SurfaceInterval = boost::icl::right_open_interval; +enum class TextureType { + Texture2D = 0, + CubeMap = 1 +}; + class SurfaceParams { public: /// Surface match traits @@ -89,6 +94,7 @@ public: u16 res_scale = 1; bool is_tiled = false; + TextureType texture_type = TextureType::Texture2D; PixelFormat pixel_format = PixelFormat::Invalid; SurfaceType type = SurfaceType::Invalid; }; diff --git a/src/video_core/rasterizer_cache/utils.h b/src/video_core/rasterizer_cache/utils.h index 1f749ae24..e9528e9e7 100644 --- a/src/video_core/rasterizer_cache/utils.h +++ b/src/video_core/rasterizer_cache/utils.h @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #pragma once -#include #include #include "common/hash.h" #include "video_core/rasterizer_cache/pixel_format.h" @@ -15,6 +14,8 @@ struct HostTextureTag { PixelFormat format{}; u32 width = 0; u32 height = 0; + u32 levels = 1; + u32 layers = 1; auto operator<=>(const HostTextureTag&) const noexcept = default; @@ -58,7 +59,7 @@ void SwizzleTexture(const SurfaceParams& params, u32 start_offset, void UnswizzleTexture(const SurfaceParams& params, u32 start_offset, std::span source_tiled, std::span dest_linear); -} // namespace OpenGL +} // namespace VideoCore namespace std { template <> diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 0c6482f11..f8fa741d6 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -727,8 +727,8 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { // Making a copy to sample from eliminates this issue and seems to be fairly cheap. OGLTexture temp_tex; if (need_duplicate_texture) { - temp_tex = runtime.Allocate2D(color_surface->GetScaledWidth(), color_surface->GetScaledHeight(), - color_surface->pixel_format); + temp_tex = runtime.Allocate(color_surface->GetScaledWidth(), color_surface->GetScaledHeight(), + color_surface->pixel_format, color_surface->texture_type); temp_tex.CopyFrom(color_surface->texture, GL_TEXTURE_2D, color_surface->max_level + 1, color_surface->GetScaledWidth(), color_surface->GetScaledHeight()); diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index 5e163ce7d..ae8cab7e1 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp @@ -52,10 +52,8 @@ void OGLTexture::Release() { handle = 0; } -void OGLTexture::Allocate(GLenum _target, GLsizei levels, GLenum internalformat, GLsizei width, +void OGLTexture::Allocate(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { - target = _target; - GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d; glActiveTexture(GL_TEXTURE0); glBindTexture(target, handle); diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index a2839993e..d6b24262c 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -65,7 +65,6 @@ public: GLsizei height); GLuint handle = 0; - GLenum target = GL_TEXTURE_2D; }; class OGLSampler : private NonCopyable { diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index c5d7d1502..ec6625731 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -124,33 +124,45 @@ const FormatTuple& TextureRuntime::GetFormatTuple(VideoCore::PixelFormat pixel_f return DEFAULT_TUPLE; } -OGLTexture TextureRuntime::Allocate2D(u32 width, u32 height, VideoCore::PixelFormat format) { - const auto& tuple = GetFormatTuple(format); - auto recycled_tex = texture_recycler.find({format, width, height}); - if (recycled_tex != texture_recycler.end()) { - OGLTexture texture = std::move(recycled_tex->second); - texture_recycler.erase(recycled_tex); +OGLTexture TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelFormat format, + VideoCore::TextureType type) { + + const u32 layers = type == VideoCore::TextureType::CubeMap ? 6 : 1; + const GLenum target = + type == VideoCore::TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + const VideoCore::HostTextureTag key = { + .format = format, + .width = width, + .height = height, + .layers = layers + }; + + // Attempt to recycle an unused texture + if (auto it = texture_recycler.find(key); it != texture_recycler.end()) { + OGLTexture texture = std::move(it->second); + texture_recycler.erase(it); return texture; } - // Allocate the 2D texture - OGLTexture texture{}; - texture.Create(); - texture.Allocate(GL_TEXTURE_2D, std::bit_width(std::max(width, height)), - tuple.internal_format, width, height); - - return texture; -} - -OGLTexture TextureRuntime::AllocateCubeMap(u32 width, VideoCore::PixelFormat format) { const auto& tuple = GetFormatTuple(format); + const OpenGLState& state = OpenGLState::GetCurState(); + GLuint old_tex = state.texture_units[0].texture_2d; - // Allocate the cube texture + // Allocate new texture OGLTexture texture{}; texture.Create(); - texture.Allocate(GL_TEXTURE_CUBE_MAP, std::bit_width(width), - tuple.internal_format, width, width); + glActiveTexture(GL_TEXTURE0); + glBindTexture(target, texture.handle); + + glTexStorage2D(target, std::bit_width(std::max(width, height)), + tuple.internal_format, width, height); + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(target, old_tex); return texture; } @@ -229,11 +241,11 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCor state.draw.draw_framebuffer = draw_fbo.handle; state.Apply(); - const GLenum src_textarget = source.is_texture_cube ? + const GLenum src_textarget = source.texture_type == VideoCore::TextureType::CubeMap ? 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); - const GLenum dst_textarget = dest.is_texture_cube ? + const GLenum dst_textarget = dest.texture_type == VideoCore::TextureType::CubeMap ? 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); @@ -287,7 +299,7 @@ void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarge Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime) : VideoCore::SurfaceBase{params}, runtime{runtime}, driver{runtime.GetDriver()} { - + texture = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), params.pixel_format, texture_type); } MICROPROFILE_DEFINE(RasterizerCache_TextureUL, "RasterizerCache", "Texture Upload", MP_RGB(128, 192, 64)); @@ -361,7 +373,8 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download) { 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); + OGLTexture unscaled_tex = runtime.Allocate(rect_width, rect_height, pixel_format, + VideoCore::TextureType::Texture2D); 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); @@ -389,7 +402,8 @@ 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); + OGLTexture unscaled_tex = runtime.Allocate(rect_width, rect_height, pixel_format, + VideoCore::TextureType::Texture2D); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, unscaled_tex.handle); diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index 4a704ed74..493d49b7b 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -70,11 +70,9 @@ public: /// Returns the OpenGL format tuple associated with the provided pixel format const FormatTuple& GetFormatTuple(VideoCore::PixelFormat pixel_format); - /// Allocates a 2D OpenGL texture with the specified dimentions and format - OGLTexture Allocate2D(u32 width, u32 height, VideoCore::PixelFormat format); - - /// Allocates an OpenGL cube map texture with the specified dimentions and format - OGLTexture AllocateCubeMap(u32 width, VideoCore::PixelFormat format); + /// Allocates an OpenGL texture with the specified dimentions and format + OGLTexture Allocate(u32 width, u32 height, VideoCore::PixelFormat format, + VideoCore::TextureType type); /// Fills the rectangle of the texture with the clear value provided bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,