// Copyright 2019 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/assert.h" #include "common/common_types.h" #include "common/scope_exit.h" #include "video_core/morton.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_texture_cache.h" #include "video_core/renderer_opengl/utils.h" #include "video_core/texture_cache/texture_cache_contextless.h" #include "video_core/textures/convert.h" #include "video_core/textures/texture.h" namespace OpenGL { using Tegra::Texture::SwizzleSource; using VideoCore::MortonSwizzleMode; namespace { struct FormatTuple { GLint internal_format; GLenum format; GLenum type; ComponentType component_type; bool compressed; }; constexpr std::array tex_format_tuples = {{ {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5U {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm, false}, // A2B10G10R10U {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5U {GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8U {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RGBA16U {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RGBA16UI {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, false}, // R11FG11FB10F {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT1 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT23 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT45 {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1 {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN2UNORM {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM {GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // BC7U {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, true}, // BC6H_UF16 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, true}, // BC6H_SF16 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8 {GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F {GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F {GL_R32F, GL_RED, GL_FLOAT, ComponentType::Float, false}, // R32F {GL_R16F, GL_RED, GL_HALF_FLOAT, ComponentType::Float, false}, // R16F {GL_R16, GL_RED, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // R16U {GL_R16_SNORM, GL_RED, GL_SHORT, ComponentType::SNorm, false}, // R16S {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // R16UI {GL_R16I, GL_RED_INTEGER, GL_SHORT, ComponentType::SInt, false}, // R16I {GL_RG16, GL_RG, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RG16 {GL_RG16F, GL_RG, GL_HALF_FLOAT, ComponentType::Float, false}, // RG16F {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RG16UI {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // RGBA8_SRGB {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8 // Compressed sRGB formats {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT1_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT23_SRGB {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT45_SRGB {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // BC7U_SRGB {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB // Depth formats {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // Z16 // DepthStencil formats {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, false}, // Z24S8 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm, false}, // S8Z24 {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, ComponentType::Float, false}, // Z32FS8 }}; const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { ASSERT(static_cast(pixel_format) < tex_format_tuples.size()); const auto& format{tex_format_tuples[static_cast(pixel_format)]}; ASSERT(component_type == format.component_type); return format; } GLenum GetTextureTarget(const SurfaceParams& params) { switch (params.GetTarget()) { case SurfaceTarget::Texture1D: return GL_TEXTURE_1D; case SurfaceTarget::Texture2D: return GL_TEXTURE_2D; case SurfaceTarget::Texture3D: return GL_TEXTURE_3D; case SurfaceTarget::Texture1DArray: return GL_TEXTURE_1D_ARRAY; case SurfaceTarget::Texture2DArray: return GL_TEXTURE_2D_ARRAY; case SurfaceTarget::TextureCubemap: return GL_TEXTURE_CUBE_MAP; case SurfaceTarget::TextureCubeArray: return GL_TEXTURE_CUBE_MAP_ARRAY; } UNREACHABLE(); return {}; } GLint GetSwizzleSource(SwizzleSource source) { switch (source) { case SwizzleSource::Zero: return GL_ZERO; case SwizzleSource::R: return GL_RED; case SwizzleSource::G: return GL_GREEN; case SwizzleSource::B: return GL_BLUE; case SwizzleSource::A: return GL_ALPHA; case SwizzleSource::OneInt: case SwizzleSource::OneFloat: return GL_ONE; } UNREACHABLE(); return GL_NONE; } void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.GetNumLevels() - 1); if (params.GetNumLevels() == 1) { glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); } } OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum internal_format) { OGLTexture texture; texture.Create(target); switch (params.GetTarget()) { case SurfaceTarget::Texture1D: glTextureStorage1D(texture.handle, params.GetNumLevels(), internal_format, params.GetWidth()); break; case SurfaceTarget::Texture2D: case SurfaceTarget::TextureCubemap: glTextureStorage2D(texture.handle, params.GetNumLevels(), internal_format, params.GetWidth(), params.GetHeight()); break; case SurfaceTarget::Texture3D: case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubeArray: glTextureStorage3D(texture.handle, params.GetNumLevels(), internal_format, params.GetWidth(), params.GetHeight(), params.GetDepth()); break; default: UNREACHABLE(); } ApplyTextureDefaults(params, texture.handle); return texture; } } // Anonymous namespace CachedSurface::CachedSurface(TextureCacheOpenGL& texture_cache, const SurfaceParams& params) : VideoCommon::SurfaceBaseContextless{texture_cache, params} { const auto& tuple{GetFormatTuple(params.GetPixelFormat(), params.GetComponentType())}; internal_format = tuple.internal_format; format = tuple.format; type = tuple.type; is_compressed = tuple.compressed; target = GetTextureTarget(params); texture = CreateTexture(params, target, internal_format); } CachedSurface::~CachedSurface() = default; void CachedSurface::DownloadTextureImpl() { // TODO(Rodrigo): Optimize alignment glPixelStorei(GL_PACK_ALIGNMENT, 1); SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); for (u32 level = 0; level < params.GetNumLevels(); ++level) { glPixelStorei(GL_PACK_ROW_LENGTH, static_cast(params.GetMipWidth(level))); if (is_compressed) { glGetCompressedTextureImage(texture.handle, level, static_cast(params.GetHostMipmapSize(level)), GetStagingBufferLevelData(level)); } else { glGetTextureImage(texture.handle, level, format, type, static_cast(params.GetHostMipmapSize(level)), GetStagingBufferLevelData(level)); } } } void CachedSurface::UploadTextureImpl() { SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); }); for (u32 level = 0; level < params.GetNumLevels(); ++level) { UploadTextureMipmap(level); } } void CachedSurface::UploadTextureMipmap(u32 level) { // TODO(Rodrigo): Optimize alignment glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(params.GetMipWidth(level))); u8* buffer{GetStagingBufferLevelData(level)}; if (is_compressed) { const auto image_size{static_cast(params.GetHostMipmapSize(level))}; switch (params.GetTarget()) { case SurfaceTarget::Texture2D: glCompressedTextureSubImage2D(texture.handle, level, 0, 0, static_cast(params.GetMipWidth(level)), static_cast(params.GetMipHeight(level)), internal_format, image_size, buffer); break; case SurfaceTarget::Texture3D: case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubeArray: glCompressedTextureSubImage3D(texture.handle, level, 0, 0, 0, static_cast(params.GetMipWidth(level)), static_cast(params.GetMipHeight(level)), static_cast(params.GetMipDepth(level)), internal_format, image_size, buffer); break; case SurfaceTarget::TextureCubemap: { const std::size_t layer_size{params.GetHostLayerSize(level)}; for (std::size_t face = 0; face < params.GetDepth(); ++face) { glCompressedTextureSubImage3D(texture.handle, level, 0, 0, static_cast(face), static_cast(params.GetMipWidth(level)), static_cast(params.GetMipHeight(level)), 1, internal_format, static_cast(layer_size), buffer); buffer += layer_size; } break; } default: UNREACHABLE(); } } else { switch (params.GetTarget()) { case SurfaceTarget::Texture1D: glTextureSubImage1D(texture.handle, level, 0, params.GetMipWidth(level), format, type, buffer); break; case SurfaceTarget::Texture1DArray: case SurfaceTarget::Texture2D: glTextureSubImage2D(texture.handle, level, 0, 0, params.GetMipWidth(level), params.GetMipHeight(level), format, type, buffer); break; case SurfaceTarget::Texture3D: case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubeArray: glTextureSubImage3D( texture.handle, level, 0, 0, 0, static_cast(params.GetMipWidth(level)), static_cast(params.GetMipHeight(level)), static_cast(params.GetMipDepth(level)), format, type, buffer); break; case SurfaceTarget::TextureCubemap: for (std::size_t face = 0; face < params.GetDepth(); ++face) { glTextureSubImage3D(texture.handle, level, 0, 0, static_cast(face), params.GetMipWidth(level), params.GetMipHeight(level), 1, format, type, buffer); buffer += params.GetHostLayerSize(level); } break; default: UNREACHABLE(); } } } void CachedSurface::DecorateSurfaceName() { LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr()); } std::unique_ptr CachedSurface::CreateView(const ViewKey& view_key) { return std::make_unique(*this, view_key); } CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, ViewKey key) : surface{surface}, key{key}, params{surface.GetSurfaceParams()} {} CachedSurfaceView::~CachedSurfaceView() = default; void CachedSurfaceView::Attach(GLenum attachment) const { ASSERT(key.num_layers == 1 && key.num_levels == 1); switch (params.GetTarget()) { case SurfaceTarget::Texture1D: glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTarget(), surface.GetTexture(), key.base_level); break; case SurfaceTarget::Texture2D: glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTarget(), surface.GetTexture(), key.base_level); break; case SurfaceTarget::Texture1DArray: case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubeArray: glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTexture(), key.base_level, key.base_layer); break; default: UNIMPLEMENTED(); } } GLuint CachedSurfaceView::GetTexture(Tegra::Shader::TextureType texture_type, bool is_array, SwizzleSource x_source, SwizzleSource y_source, SwizzleSource z_source, SwizzleSource w_source) { const auto [texture_view, target] = GetTextureView(texture_type, is_array); if (texture_view.get().texture.handle == 0) { texture_view.get() = std::move(CreateTextureView(target)); } ApplySwizzle(texture_view, x_source, y_source, z_source, w_source); return texture_view.get().texture.handle; } void CachedSurfaceView::ApplySwizzle(TextureView& texture_view, SwizzleSource x_source, SwizzleSource y_source, SwizzleSource z_source, SwizzleSource w_source) { const std::array swizzle = {x_source, y_source, z_source, w_source}; if (swizzle == texture_view.swizzle) { return; } const std::array gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), GetSwizzleSource(z_source), GetSwizzleSource(w_source)}; glTextureParameteriv(texture_view.texture.handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); texture_view.swizzle = swizzle; } CachedSurfaceView::TextureView CachedSurfaceView::CreateTextureView(GLenum target) const { TextureView texture_view; glGenTextures(1, &texture_view.texture.handle); const GLuint handle{texture_view.texture.handle}; const FormatTuple& tuple{GetFormatTuple(params.GetPixelFormat(), params.GetComponentType())}; glTextureView(handle, target, surface.texture.handle, tuple.internal_format, key.base_level, key.num_levels, key.base_layer, key.num_layers); ApplyTextureDefaults(params, handle); return texture_view; } std::pair, GLenum> CachedSurfaceView::GetTextureView(Tegra::Shader::TextureType texture_type, bool is_array) { using Pair = std::pair, GLenum>; switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: return is_array ? Pair{texture_view_1d_array, GL_TEXTURE_1D_ARRAY} : Pair{texture_view_1d, GL_TEXTURE_1D}; case Tegra::Shader::TextureType::Texture2D: return is_array ? Pair{texture_view_2d_array, GL_TEXTURE_2D_ARRAY} : Pair{texture_view_2d, GL_TEXTURE_2D}; case Tegra::Shader::TextureType::Texture3D: ASSERT(!is_array); return {texture_view_3d, GL_TEXTURE_3D}; case Tegra::Shader::TextureType::TextureCube: return is_array ? Pair{texture_view_cube_array, GL_TEXTURE_CUBE_MAP_ARRAY} : Pair{texture_view_cube, GL_TEXTURE_CUBE_MAP}; } UNREACHABLE(); } TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer) : TextureCacheBase{system, rasterizer} {} TextureCacheOpenGL::~TextureCacheOpenGL() = default; CachedSurfaceView* TextureCacheOpenGL::TryFastGetSurfaceView(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& new_params, bool preserve_contents, const std::vector& overlaps) { if (overlaps.size() > 1) { return TryCopyAsViews(gpu_addr, cpu_addr, host_ptr, new_params, overlaps); } const auto& old_surface{overlaps[0]}; const auto& old_params{old_surface->GetSurfaceParams()}; if (old_params.GetTarget() == new_params.GetTarget() && old_params.GetDepth() == new_params.GetDepth() && old_params.GetDepth() == 1 && old_params.GetNumLevels() == new_params.GetNumLevels() && old_params.GetPixelFormat() == new_params.GetPixelFormat()) { return SurfaceCopy(gpu_addr, cpu_addr, host_ptr, new_params, old_surface, old_params); } return nullptr; } CachedSurfaceView* TextureCacheOpenGL::SurfaceCopy(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& new_params, const Surface& old_surface, const SurfaceParams& old_params) { const auto new_surface{GetUncachedSurface(new_params)}; Register(new_surface, gpu_addr, cpu_addr, host_ptr); const u32 min_width{ std::max(old_params.GetDefaultBlockWidth(), new_params.GetDefaultBlockWidth())}; const u32 min_height{ std::max(old_params.GetDefaultBlockHeight(), new_params.GetDefaultBlockHeight())}; for (u32 level = 0; level < old_params.GetNumLevels(); ++level) { const u32 width{std::min(old_params.GetMipWidth(level), new_params.GetMipWidth(level))}; const u32 height{std::min(old_params.GetMipHeight(level), new_params.GetMipHeight(level))}; if (width < min_width || height < min_height) { // Avoid copies that are too small to be handled in OpenGL break; } glCopyImageSubData(old_surface->GetTexture(), old_surface->GetTarget(), level, 0, 0, 0, new_surface->GetTexture(), new_surface->GetTarget(), level, 0, 0, 0, width, height, 1); } new_surface->MarkAsModified(true); // TODO(Rodrigo): Add an entry to directly get the superview return new_surface->GetView(gpu_addr, new_params); } CachedSurfaceView* TextureCacheOpenGL::TryCopyAsViews(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& new_params, const std::vector& overlaps) { if (new_params.GetTarget() == SurfaceTarget::Texture1D || new_params.GetTarget() == SurfaceTarget::Texture1DArray || new_params.GetTarget() == SurfaceTarget::Texture3D) { // Non-2D textures are not handled at the moment in this fast path. return nullptr; } const auto new_surface{GetUncachedSurface(new_params)}; // TODO(Rodrigo): Move this down Register(new_surface, gpu_addr, cpu_addr, host_ptr); // TODO(Rodrigo): Find a way to avoid heap allocations here. std::vector views; views.reserve(overlaps.size()); for (const auto& overlap : overlaps) { const auto view{ new_surface->TryGetView(overlap->GetGpuAddr(), overlap->GetSurfaceParams())}; if (!view) { // TODO(Rodrigo): Remove this Unregister(new_surface); return nullptr; } views.push_back(view); } // TODO(Rodrigo): It's possible that these method leaves some unloaded textures if the data has // been uploaded to guest memory but not used as a surface previously. for (std::size_t i = 0; i < overlaps.size(); ++i) { const auto& overlap{overlaps[i]}; const auto& view{views[i]}; for (u32 overlap_level = 0; overlap_level < view->GetNumLevels(); ++overlap_level) { const u32 super_level{view->GetBaseLevel() + overlap_level}; glCopyImageSubData(overlap->GetTexture(), overlap->GetTarget(), overlap_level, 0, 0, 0, new_surface->GetTexture(), new_surface->GetTarget(), super_level, 0, 0, view->GetBaseLayer(), view->GetWidth(), view->GetHeight(), view->GetNumLayers()); } } new_surface->MarkAsModified(true); // TODO(Rodrigo): Add an entry to directly get the superview return new_surface->GetView(gpu_addr, new_params); } Surface TextureCacheOpenGL::CreateSurface(const SurfaceParams& params) { return std::make_unique(*this, params); } } // namespace OpenGL