From bbf3b2da0cee61ee99cdc42d08543881640990e4 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Wed, 24 Oct 2018 18:30:27 -0400 Subject: [PATCH 1/7] Implement Mipmaps --- .../renderer_opengl/gl_rasterizer_cache.cpp | 242 +++++++++++------- .../renderer_opengl/gl_rasterizer_cache.h | 72 +++++- 2 files changed, 212 insertions(+), 102 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index b057e2efa3..70d1ebda5f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -90,27 +90,33 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { } } -std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { +std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only, + bool uncompressed) const { const u32 compression_factor{GetCompressionFactor(pixel_format)}; const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; u32 m_depth = (layer_only ? 1U : depth); - u32 m_width = std::max(1U, width / compression_factor); - u32 m_height = std::max(1U, height / compression_factor); - std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, - m_depth, block_height, block_depth); - u32 m_block_height = block_height; - u32 m_block_depth = block_depth; + u32 m_width = uncompressed ? width : std::max(1U, width / compression_factor); + u32 m_height = uncompressed ? height : std::max(1U, height / compression_factor); + m_width = std::max(1U, m_width >> mip_level); + m_height = std::max(1U, m_height >> mip_level); + m_depth = std::max(1U, m_depth >> mip_level); + u32 m_block_height = MipBlockHeight(mip_level); + u32 m_block_depth = MipBlockDepth(mip_level); + return Tegra::Texture::CalculateSize(force_gl ? false : is_tiled, bytes_per_pixel, m_width, + m_height, m_depth, m_block_height, m_block_depth); +} + +std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, + bool uncompressed) const { std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size - for (u32 i = 1; i < max_mip_level; i++) { - m_width = std::max(1U, m_width / 2); - m_height = std::max(1U, m_height / 2); - m_depth = std::max(1U, m_depth / 2); - m_block_height = std::max(1U, m_block_height / 2); - m_block_depth = std::max(1U, m_block_depth / 2); - size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth, - m_block_height, m_block_depth); + std::size_t size = 0; + for (u32 i = 0; i < max_mip_level; i++) { + size += InnerMipmapMemorySize(i, force_gl, layer_only, uncompressed); } - return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; + if (!force_gl && is_tiled) { + size = Common::AlignUp(size, block_size_bytes); + } + return size; } /*static*/ SurfaceParams SurfaceParams::CreateForTexture( @@ -188,7 +194,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.unaligned_height = config.height; params.target = SurfaceTarget::Texture2D; params.depth = 1; - params.max_mip_level = 0; + params.max_mip_level = 1; params.is_layered = false; // Render target specific parameters, not used for caching @@ -222,7 +228,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.unaligned_height = zeta_height; params.target = SurfaceTarget::Texture2D; params.depth = 1; - params.max_mip_level = 0; + params.max_mip_level = 1; params.is_layered = false; params.rt = {}; @@ -249,7 +255,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.unaligned_height = config.height; params.target = SurfaceTarget::Texture2D; params.depth = 1; - params.max_mip_level = 0; + params.max_mip_level = 1; params.rt = {}; params.InitCacheParameters(config.Address()); @@ -373,13 +379,13 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType return format; } -MathUtil::Rectangle SurfaceParams::GetRect() const { - u32 actual_height{unaligned_height}; +MathUtil::Rectangle SurfaceParams::GetRect(u32 mip_level) const { + u32 actual_height{std::max(1U, unaligned_height >> mip_level)}; if (IsPixelFormatASTC(pixel_format)) { // ASTC formats must stop at the ATSC block size boundary actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second); } - return {0, actual_height, width, 0}; + return {0, actual_height, MipWidth(mip_level), 0}; } /// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN @@ -563,28 +569,31 @@ static constexpr GLConversionArray gl_to_morton_fns = { }; void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, - std::vector& gl_buffer) { - u32 depth = params.depth; + std::vector& gl_buffer, u32 mip_level) { + u32 depth = params.MipDepth(mip_level); if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. depth = 1U; } if (params.is_layered) { - u64 offset = 0; + u64 offset = params.GetMipmapLevelOffset(mip_level); u64 offset_gl = 0; u64 layer_size = params.LayerMemorySize(); - u64 gl_size = params.LayerSizeGL(); - for (u32 i = 0; i < depth; i++) { + u64 gl_size = params.LayerSizeGL(mip_level); + for (u32 i = 0; i < params.depth; i++) { functions[static_cast(params.pixel_format)]( - params.width, params.block_height, params.height, params.block_depth, 1, + params.MipWidth(mip_level), params.MipBlockHeight(mip_level), + params.MipHeight(mip_level), params.MipBlockDepth(mip_level), 1, gl_buffer.data() + offset_gl, gl_size, params.addr + offset); offset += layer_size; offset_gl += gl_size; } } else { + u64 offset = params.GetMipmapLevelOffset(mip_level); functions[static_cast(params.pixel_format)]( - params.width, params.block_height, params.height, params.block_depth, depth, - gl_buffer.data(), gl_buffer.size(), params.addr); + params.MipWidth(mip_level), params.MipBlockHeight(mip_level), + params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(), + gl_buffer.size(), params.addr + offset); } } @@ -839,29 +848,31 @@ CachedSurface::CachedSurface(const SurfaceParams& params) // Only pre-create the texture for non-compressed textures. switch (params.target) { case SurfaceParams::SurfaceTarget::Texture1D: - glTexStorage1D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, - rect.GetWidth()); + glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level, + format_tuple.internal_format, rect.GetWidth()); break; case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceParams::SurfaceTarget::TextureCubemap: - glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, - rect.GetWidth(), rect.GetHeight()); + glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level, + format_tuple.internal_format, rect.GetWidth(), rect.GetHeight()); break; case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceParams::SurfaceTarget::Texture2DArray: - glTexStorage3D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, - rect.GetWidth(), rect.GetHeight(), params.depth); + glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level, + format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(), + params.depth); break; default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast(params.target)); UNREACHABLE(); - glTexStorage2D(GL_TEXTURE_2D, 1, format_tuple.internal_format, rect.GetWidth(), - rect.GetHeight()); + glTexStorage2D(GL_TEXTURE_2D, params.max_mip_level, format_tuple.internal_format, + rect.GetWidth(), rect.GetHeight()); } } glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -992,20 +1003,22 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector& data, PixelForm MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); void CachedSurface::LoadGLBuffer() { MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); - - gl_buffer.resize(params.size_in_bytes_gl); + gl_buffer.resize(params.max_mip_level); + for (u32 i = 0; i < params.max_mip_level; i++) + gl_buffer[i].resize(params.GetMipmapSizeGL(i)); if (params.is_tiled) { ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast(params.target)); - - SwizzleFunc(morton_to_gl_fns, params, gl_buffer); + for (u32 i = 0; i < params.max_mip_level; i++) + SwizzleFunc(morton_to_gl_fns, params, gl_buffer[i], i); } else { const auto texture_src_data{Memory::GetPointer(params.addr)}; const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; - gl_buffer.assign(texture_src_data, texture_src_data_end); + gl_buffer[0].assign(texture_src_data, texture_src_data_end); } - - ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); + for (u32 i = 0; i < params.max_mip_level; i++) + ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i), + params.MipHeight(i)); } MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); @@ -1015,7 +1028,8 @@ void CachedSurface::FlushGLBuffer() { ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented"); // OpenGL temporary buffer needs to be big enough to store raw texture size - gl_buffer.resize(GetSizeInBytes()); + gl_buffer.resize(1); + gl_buffer[0].resize(GetSizeInBytes()); const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT @@ -1023,10 +1037,10 @@ void CachedSurface::FlushGLBuffer() { glPixelStorei(GL_PACK_ROW_LENGTH, static_cast(params.width)); ASSERT(!tuple.compressed); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, - static_cast(gl_buffer.size()), gl_buffer.data()); + glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, static_cast(gl_buffer[0].size()), + gl_buffer[0].data()); glPixelStorei(GL_PACK_ROW_LENGTH, 0); - ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, + ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width, params.height); ASSERT(params.type != SurfaceType::Fill); const u8* const texture_src_data = Memory::GetPointer(params.addr); @@ -1035,26 +1049,21 @@ void CachedSurface::FlushGLBuffer() { ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast(params.target)); - SwizzleFunc(gl_to_morton_fns, params, gl_buffer); + SwizzleFunc(gl_to_morton_fns, params, gl_buffer[0], 0); } else { - std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); + std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes()); } } -MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); -void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { - if (params.type == SurfaceType::Fill) - return; - - MICROPROFILE_SCOPE(OpenGL_TextureUL); - - const auto& rect{params.GetRect()}; +void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, + GLuint draw_fb_handle) { + const auto& rect{params.GetRect(mip_map)}; // Load data from memory to the surface const GLint x0 = static_cast(rect.left); const GLint y0 = static_cast(rect.bottom); std::size_t buffer_offset = - static_cast(static_cast(y0) * params.width + + static_cast(static_cast(y0) * params.MipWidth(mip_map) + static_cast(x0)) * SurfaceParams::GetBytesPerPixel(params.pixel_format); @@ -1072,88 +1081,131 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle cur_state.Apply(); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT - ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(params.width)); + ASSERT(params.MipWidth(mip_map) * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == + 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(params.MipWidth(mip_map))); + GLsizei image_size = static_cast(params.GetMipmapSizeGL(mip_map, false)); glActiveTexture(GL_TEXTURE0); if (tuple.compressed) { switch (params.target) { case SurfaceParams::SurfaceTarget::Texture2D: - glCompressedTexImage2D( - SurfaceTargetToGL(params.target), 0, tuple.internal_format, - static_cast(params.width), static_cast(params.height), 0, - static_cast(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); + glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, + static_cast(params.MipWidth(mip_map)), + static_cast(params.MipHeight(mip_map)), 0, image_size, + &gl_buffer[mip_map][buffer_offset]); break; case SurfaceParams::SurfaceTarget::Texture3D: - case SurfaceParams::SurfaceTarget::Texture2DArray: - glCompressedTexImage3D( - SurfaceTargetToGL(params.target), 0, tuple.internal_format, - static_cast(params.width), static_cast(params.height), - static_cast(params.depth), 0, - static_cast(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); + glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, + static_cast(params.MipWidth(mip_map)), + static_cast(params.MipHeight(mip_map)), + static_cast(params.MipDepth(mip_map)), 0, image_size, + &gl_buffer[mip_map][buffer_offset]); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceParams::SurfaceTarget::Texture2DArray: + glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, + static_cast(params.MipWidth(mip_map)), + static_cast(params.MipHeight(mip_map)), + static_cast(params.depth), 0, image_size, + &gl_buffer[mip_map][buffer_offset]); + break; + case SurfaceParams::SurfaceTarget::TextureCubemap: { + GLsizei layer_size = static_cast(params.LayerSizeGL(mip_map)); for (std::size_t face = 0; face < params.depth; ++face) { glCompressedTexImage2D(static_cast(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), - 0, tuple.internal_format, static_cast(params.width), - static_cast(params.height), 0, - static_cast(params.SizeInBytesCubeFaceGL()), - &gl_buffer[buffer_offset]); - buffer_offset += params.SizeInBytesCubeFace(); + mip_map, tuple.internal_format, + static_cast(params.MipWidth(mip_map)), + static_cast(params.MipHeight(mip_map)), 0, + layer_size, &gl_buffer[mip_map][buffer_offset]); + buffer_offset += layer_size; } break; + } default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast(params.target)); UNREACHABLE(); - glCompressedTexImage2D( - GL_TEXTURE_2D, 0, tuple.internal_format, static_cast(params.width), - static_cast(params.height), 0, - static_cast(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); + glCompressedTexImage2D(GL_TEXTURE_2D, mip_map, tuple.internal_format, + static_cast(params.MipWidth(mip_map)), + static_cast(params.MipHeight(mip_map)), 0, + static_cast(params.size_in_bytes_gl), + &gl_buffer[mip_map][buffer_offset]); } } else { switch (params.target) { case SurfaceParams::SurfaceTarget::Texture1D: - glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0, + glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0, static_cast(rect.GetWidth()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); + &gl_buffer[mip_map][buffer_offset]); break; case SurfaceParams::SurfaceTarget::Texture2D: - glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0, + glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0, static_cast(rect.GetWidth()), static_cast(rect.GetHeight()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); + &gl_buffer[mip_map][buffer_offset]); break; case SurfaceParams::SurfaceTarget::Texture3D: + glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, + static_cast(rect.GetWidth()), + static_cast(rect.GetHeight()), params.MipDepth(mip_map), + tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); + break; case SurfaceParams::SurfaceTarget::Texture2DArray: - glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0, + glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, static_cast(rect.GetWidth()), static_cast(rect.GetHeight()), params.depth, tuple.format, - tuple.type, &gl_buffer[buffer_offset]); + tuple.type, &gl_buffer[mip_map][buffer_offset]); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceParams::SurfaceTarget::TextureCubemap: { + std::size_t start = buffer_offset; for (std::size_t face = 0; face < params.depth; ++face) { - glTexSubImage2D(static_cast(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, x0, - y0, static_cast(rect.GetWidth()), + glTexSubImage2D(static_cast(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map, + x0, y0, static_cast(rect.GetWidth()), static_cast(rect.GetHeight()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); - buffer_offset += params.SizeInBytesCubeFace(); + &gl_buffer[mip_map][buffer_offset]); + buffer_offset += params.LayerSizeGL(mip_map); } break; + } default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast(params.target)); UNREACHABLE(); - glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast(rect.GetWidth()), + glTexSubImage2D(GL_TEXTURE_2D, mip_map, x0, y0, static_cast(rect.GetWidth()), static_cast(rect.GetHeight()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); + &gl_buffer[mip_map][buffer_offset]); } } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } +MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); +void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { + if (params.type == SurfaceType::Fill) + return; + + MICROPROFILE_SCOPE(OpenGL_TextureUL); + + for (u32 i = 0; i < params.max_mip_level; i++) + UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); + + if (params.max_mip_level == 1) { + const GLuint target_tex = texture.handle; + OpenGLState cur_state = OpenGLState::GetCurState(); + const auto& old_tex = cur_state.texture_units[0]; + SCOPE_EXIT({ + cur_state.texture_units[0] = old_tex; + cur_state.Apply(); + }); + cur_state.texture_units[0].texture = target_tex; + cur_state.texture_units[0].target = SurfaceTargetToGL(params.target); + cur_state.Apply(); + glGenerateMipmap(SurfaceTargetToGL(params.target)); + } +} + RasterizerCacheOpenGL::RasterizerCacheOpenGL() { read_framebuffer.Create(); draw_framebuffer.Create(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index be8c00e993..5bcd331566 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -834,7 +834,7 @@ struct SurfaceParams { } /// Returns the rectangle corresponding to this surface - MathUtil::Rectangle GetRect() const; + MathUtil::Rectangle GetRect(u32 mip_level = 0) const; /// Returns the total size of this surface in bytes, adjusted for compression std::size_t SizeInBytesRaw(bool ignore_tiled = false) const { @@ -865,7 +865,7 @@ struct SurfaceParams { /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps. std::size_t MemorySize() const { - std::size_t size = InnerMemorySize(is_layered); + std::size_t size = InnerMemorySize(false, is_layered); if (is_layered) return size * depth; return size; @@ -874,12 +874,65 @@ struct SurfaceParams { /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including /// mipmaps. std::size_t LayerMemorySize() const { - return InnerMemorySize(true); + return InnerMemorySize(false, true); } /// Returns the size of a layer of this surface in OpenGL. - std::size_t LayerSizeGL() const { - return SizeInBytesRaw(true) / depth; + std::size_t LayerSizeGL(u32 mip_level) const { + return InnerMipmapMemorySize(mip_level, true, is_layered, false); + } + + std::size_t GetMipmapSizeGL(u32 mip_level, bool ignore_compressed = true) const { + std::size_t size = InnerMipmapMemorySize(mip_level, true, is_layered, ignore_compressed); + if (is_layered) + return size * depth; + return size; + } + + std::size_t GetMipmapLevelOffset(u32 mip_level) const { + std::size_t offset = 0; + for (u32 i = 0; i < mip_level; i++) + offset += InnerMipmapMemorySize(i, false, is_layered); + return offset; + } + + std::size_t GetMipmapLevelOffsetGL(u32 mip_level) const { + std::size_t offset = 0; + for (u32 i = 0; i < mip_level; i++) + offset += InnerMipmapMemorySize(i, true, is_layered); + return offset; + } + + u32 MipWidth(u32 mip_level) const { + return std::max(1U, width >> mip_level); + } + + u32 MipHeight(u32 mip_level) const { + return std::max(1U, height >> mip_level); + } + + u32 MipDepth(u32 mip_level) const { + return std::max(1U, depth >> mip_level); + } + + u32 MipBlockHeight(u32 mip_level) const { + u32 height = MipHeight(mip_level); + u32 bh = block_height; + // Magical block resizing algorithm, needs more testing. + while (bh != 1 && height / bh <= 16) { + bh = bh >> 1; + } + return bh; + } + + u32 MipBlockDepth(u32 mip_level) const { + u32 depth = MipDepth(mip_level); + u32 bd = block_depth; + // Magical block resizing algorithm, needs more testing. + while (bd != 1 && depth / bd <= 16) { + bd = bd >> 1; + } + return bd; } /// Creates SurfaceParams from a texture configuration @@ -940,7 +993,10 @@ struct SurfaceParams { } rt; private: - std::size_t InnerMemorySize(bool layer_only = false) const; + std::size_t InnerMipmapMemorySize(u32 mip_level, bool force_gl = false, bool layer_only = false, + bool uncompressed = false) const; + std::size_t InnerMemorySize(bool force_gl = false, bool layer_only = false, + bool uncompressed = false) const; }; }; // namespace OpenGL @@ -1002,8 +1058,10 @@ public: void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); private: + void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle); + OGLTexture texture; - std::vector gl_buffer; + std::vector> gl_buffer; SurfaceParams params; GLenum gl_target; std::size_t cached_size_in_bytes; From dc85e3bff16c375a909617908c9c7b82ec61cbdd Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Wed, 24 Oct 2018 18:44:37 -0400 Subject: [PATCH 2/7] Zero out memory region of recreated surface before flushing --- src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 70d1ebda5f..28f17bc75d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1325,6 +1325,8 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface) { const auto& src_params{src_surface->GetSurfaceParams()}; const auto& dst_params{dst_surface->GetSurfaceParams()}; + auto* start = Memory::GetPointer(src_params.addr); + std::fill(start, start + dst_params.MemorySize(), 0); FlushRegion(src_params.addr, dst_params.MemorySize()); LoadSurface(dst_surface); } From 258f0f5c31decbb25eddde798b3a4bcc6a7cc6dd Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Wed, 24 Oct 2018 19:25:28 -0400 Subject: [PATCH 3/7] Implement Mip Filter --- .../renderer_opengl/gl_rasterizer.cpp | 10 +++++-- .../renderer_opengl/gl_rasterizer.h | 1 + .../renderer_opengl/gl_rasterizer_cache.h | 4 +-- .../renderer_opengl/maxwell_to_gl.h | 28 +++++++++++++++---- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index cb180b93cf..7bb5544fc0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -731,11 +731,15 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr if (mag_filter != config.mag_filter) { mag_filter = config.mag_filter; - glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, MaxwellToGL::TextureFilterMode(mag_filter)); + glSamplerParameteri( + s, GL_TEXTURE_MAG_FILTER, + MaxwellToGL::TextureFilterMode(mag_filter, Tegra::Texture::TextureMipmapFilter::None)); } - if (min_filter != config.min_filter) { + if (min_filter != config.min_filter || mip_filter != config.mip_filter) { min_filter = config.min_filter; - glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, MaxwellToGL::TextureFilterMode(min_filter)); + mip_filter = config.mip_filter; + glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, + MaxwellToGL::TextureFilterMode(min_filter, mip_filter)); } if (wrap_u != config.wrap_u) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 5020a5392b..7b0615125b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -93,6 +93,7 @@ private: private: Tegra::Texture::TextureFilter mag_filter; Tegra::Texture::TextureFilter min_filter; + Tegra::Texture::TextureMipmapFilter mip_filter; Tegra::Texture::WrapMode wrap_u; Tegra::Texture::WrapMode wrap_v; Tegra::Texture::WrapMode wrap_p; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 5bcd331566..68479d55ac 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -919,7 +919,7 @@ struct SurfaceParams { u32 height = MipHeight(mip_level); u32 bh = block_height; // Magical block resizing algorithm, needs more testing. - while (bh != 1 && height / bh <= 16) { + while (bh > 1 && (height + bh - 1) / bh <= 16) { bh = bh >> 1; } return bh; @@ -929,7 +929,7 @@ struct SurfaceParams { u32 depth = MipDepth(mip_level); u32 bd = block_depth; // Magical block resizing algorithm, needs more testing. - while (bd != 1 && depth / bd <= 16) { + while (bd > 1 && depth / bd <= 16) { bd = bd >> 1; } return bd; diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 0f6dcab2b5..76c3b65bdc 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -135,12 +135,29 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { return {}; } -inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { +inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, + Tegra::Texture::TextureMipmapFilter mip_filter_mode) { switch (filter_mode) { - case Tegra::Texture::TextureFilter::Linear: - return GL_LINEAR; - case Tegra::Texture::TextureFilter::Nearest: - return GL_NEAREST; + case Tegra::Texture::TextureFilter::Linear: { + switch (mip_filter_mode) { + case Tegra::Texture::TextureMipmapFilter::None: + return GL_LINEAR; + case Tegra::Texture::TextureMipmapFilter::Nearest: + return GL_NEAREST_MIPMAP_LINEAR; + case Tegra::Texture::TextureMipmapFilter::Linear: + return GL_LINEAR_MIPMAP_LINEAR; + } + } + case Tegra::Texture::TextureFilter::Nearest: { + switch (mip_filter_mode) { + case Tegra::Texture::TextureMipmapFilter::None: + return GL_NEAREST; + case Tegra::Texture::TextureMipmapFilter::Nearest: + return GL_NEAREST_MIPMAP_NEAREST; + case Tegra::Texture::TextureMipmapFilter::Linear: + return GL_LINEAR_MIPMAP_NEAREST; + } + } } LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast(filter_mode)); @@ -148,6 +165,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { return {}; } + inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { switch (wrap_mode) { case Tegra::Texture::WrapMode::Wrap: From f4432b5d0cb0cb89ff4af8172720f7457514d981 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Thu, 25 Oct 2018 12:24:10 -0400 Subject: [PATCH 4/7] Fixed Block Resizing algorithm and Clang Format --- .../renderer_opengl/gl_rasterizer_cache.cpp | 7 +++--- .../renderer_opengl/gl_rasterizer_cache.h | 23 ++++++++++++------- .../renderer_opengl/maxwell_to_gl.h | 1 - 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 28f17bc75d..cbe5bf6649 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -100,7 +100,7 @@ std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, b m_width = std::max(1U, m_width >> mip_level); m_height = std::max(1U, m_height >> mip_level); m_depth = std::max(1U, m_depth >> mip_level); - u32 m_block_height = MipBlockHeight(mip_level); + u32 m_block_height = MipBlockHeight(mip_level, m_height); u32 m_block_depth = MipBlockDepth(mip_level); return Tegra::Texture::CalculateSize(force_gl ? false : is_tiled, bytes_per_pixel, m_width, m_height, m_depth, m_block_height, m_block_depth); @@ -875,6 +875,9 @@ CachedSurface::CachedSurface(const SurfaceParams& params) glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (params.max_mip_level == 1) { + glTexParameterf(SurfaceTargetToGL(params.target), GL_TEXTURE_LOD_BIAS, 1000.0); + } VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, SurfaceParams::SurfaceTargetName(params.target)); @@ -1325,8 +1328,6 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface) { const auto& src_params{src_surface->GetSurfaceParams()}; const auto& dst_params{dst_surface->GetSurfaceParams()}; - auto* start = Memory::GetPointer(src_params.addr); - std::fill(start, start + dst_params.MemorySize(), 0); FlushRegion(src_params.addr, dst_params.MemorySize()); LoadSurface(dst_surface); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 68479d55ac..951e03ba60 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -915,21 +915,28 @@ struct SurfaceParams { return std::max(1U, depth >> mip_level); } - u32 MipBlockHeight(u32 mip_level) const { - u32 height = MipHeight(mip_level); - u32 bh = block_height; - // Magical block resizing algorithm, needs more testing. - while (bh > 1 && (height + bh - 1) / bh <= 16) { - bh = bh >> 1; + // Auto block resizing algorithm from: + // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c + u32 MipBlockHeight(u32 mip_level, u32 alt_height = 0) const { + if (mip_level == 0) + return block_height; + if (alt_height == 0) + alt_height = MipHeight(mip_level); + u32 blocks_in_y = (alt_height + 7) / 8; + u32 bh = 32; + while (bh > 1 && blocks_in_y <= bh * 2) { + bh >>= 1; } return bh; } u32 MipBlockDepth(u32 mip_level) const { + if (mip_level == 0) + return block_depth; u32 depth = MipDepth(mip_level); - u32 bd = block_depth; + u32 bd = 32; // Magical block resizing algorithm, needs more testing. - while (bd > 1 && depth / bd <= 16) { + while (bd > 1 && depth / depth <= bd) { bd = bd >> 1; } return bd; diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 76c3b65bdc..87d511c381 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -165,7 +165,6 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, return {}; } - inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { switch (wrap_mode) { case Tegra::Texture::WrapMode::Wrap: From 87f8181405cd09da15e7d1eb4a64024431fc0957 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Fri, 26 Oct 2018 16:17:29 -0400 Subject: [PATCH 5/7] Fixed Invalid Image size and Mipmap calculation --- .../renderer_opengl/gl_rasterizer_cache.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index cbe5bf6649..4d89d0f671 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -95,10 +95,13 @@ std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, b const u32 compression_factor{GetCompressionFactor(pixel_format)}; const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; u32 m_depth = (layer_only ? 1U : depth); - u32 m_width = uncompressed ? width : std::max(1U, width / compression_factor); - u32 m_height = uncompressed ? height : std::max(1U, height / compression_factor); - m_width = std::max(1U, m_width >> mip_level); - m_height = std::max(1U, m_height >> mip_level); + u32 m_width = MipWidth(mip_level); + u32 m_height = MipHeight(mip_level); + m_width = uncompressed ? m_width + : std::max(1U, (m_width + compression_factor - 1) / compression_factor); + m_height = uncompressed + ? m_height + : std::max(1U, (m_height + compression_factor - 1) / compression_factor); m_depth = std::max(1U, m_depth >> mip_level); u32 m_block_height = MipBlockHeight(mip_level, m_height); u32 m_block_depth = MipBlockDepth(mip_level); From f0e902a7d6df72531fbd01d27756f9875dc3c65d Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Fri, 26 Oct 2018 19:18:03 -0400 Subject: [PATCH 6/7] Fixed mipmap block autosizing algorithm --- .../renderer_opengl/gl_rasterizer_cache.cpp | 8 +++---- .../renderer_opengl/gl_rasterizer_cache.h | 24 ++++++++++++------- src/video_core/textures/decoders.h | 6 +++++ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 4d89d0f671..b2250e6bed 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -103,7 +103,7 @@ std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, b ? m_height : std::max(1U, (m_height + compression_factor - 1) / compression_factor); m_depth = std::max(1U, m_depth >> mip_level); - u32 m_block_height = MipBlockHeight(mip_level, m_height); + u32 m_block_height = MipBlockHeight(mip_level); u32 m_block_depth = MipBlockDepth(mip_level); return Tegra::Texture::CalculateSize(force_gl ? false : is_tiled, bytes_per_pixel, m_width, m_height, m_depth, m_block_height, m_block_depth); @@ -111,7 +111,7 @@ std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, b std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, bool uncompressed) const { - std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size + std::size_t block_size_bytes = Tegra::Texture::GetGOBSize() * block_height * block_depth; std::size_t size = 0; for (u32 i = 0; i < max_mip_level; i++) { size += InnerMipmapMemorySize(i, force_gl, layer_only, uncompressed); @@ -1043,8 +1043,8 @@ void CachedSurface::FlushGLBuffer() { glPixelStorei(GL_PACK_ROW_LENGTH, static_cast(params.width)); ASSERT(!tuple.compressed); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, static_cast(gl_buffer[0].size()), - gl_buffer[0].data()); + glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, + static_cast(gl_buffer[0].size()), gl_buffer[0].data()); glPixelStorei(GL_PACK_ROW_LENGTH, 0); ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width, params.height); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 951e03ba60..15ac4a1b16 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -917,14 +917,14 @@ struct SurfaceParams { // Auto block resizing algorithm from: // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c - u32 MipBlockHeight(u32 mip_level, u32 alt_height = 0) const { + u32 MipBlockHeight(u32 mip_level) const { if (mip_level == 0) return block_height; - if (alt_height == 0) - alt_height = MipHeight(mip_level); - u32 blocks_in_y = (alt_height + 7) / 8; - u32 bh = 32; - while (bh > 1 && blocks_in_y <= bh * 2) { + u32 alt_height = MipHeight(mip_level); + u32 h = GetDefaultBlockHeight(pixel_format); + u32 blocks_in_y = (alt_height + h - 1) / h; + u32 bh = 16; + while (bh > 1 && blocks_in_y <= bh * 4) { bh >>= 1; } return bh; @@ -933,11 +933,17 @@ struct SurfaceParams { u32 MipBlockDepth(u32 mip_level) const { if (mip_level == 0) return block_depth; + if (is_layered) + return 1; u32 depth = MipDepth(mip_level); u32 bd = 32; - // Magical block resizing algorithm, needs more testing. - while (bd > 1 && depth / depth <= bd) { - bd = bd >> 1; + while (bd > 1 && depth * 2 <= bd) { + bd >>= 1; + } + if (bd == 32) { + u32 bh = MipBlockHeight(mip_level); + if (bh >= 4) + return 16; } return bd; } diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 4726f54a5e..b390219e4b 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -10,6 +10,12 @@ namespace Tegra::Texture { +// GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents +// an small rect of (64/bytes_per_pixel)X8. +inline std::size_t GetGOBSize() { + return 512; +} + /** * Unswizzles a swizzled texture without changing its format. */ From dde3094058358cfeaa436b4324adda305b4570bf Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Sun, 28 Oct 2018 18:58:07 -0400 Subject: [PATCH 7/7] Fixed black textures, pixelation and we no longer require to auto-generate mipmaps --- .../renderer_opengl/gl_rasterizer_cache.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index b2250e6bed..81b86b7a5a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -878,6 +878,8 @@ CachedSurface::CachedSurface(const SurfaceParams& params) glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL, + params.max_mip_level - 1); if (params.max_mip_level == 1) { glTexParameterf(SurfaceTargetToGL(params.target), GL_TEXTURE_LOD_BIAS, 1000.0); } @@ -1196,20 +1198,6 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle for (u32 i = 0; i < params.max_mip_level; i++) UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); - - if (params.max_mip_level == 1) { - const GLuint target_tex = texture.handle; - OpenGLState cur_state = OpenGLState::GetCurState(); - const auto& old_tex = cur_state.texture_units[0]; - SCOPE_EXIT({ - cur_state.texture_units[0] = old_tex; - cur_state.Apply(); - }); - cur_state.texture_units[0].texture = target_tex; - cur_state.texture_units[0].target = SurfaceTargetToGL(params.target); - cur_state.Apply(); - glGenerateMipmap(SurfaceTargetToGL(params.target)); - } } RasterizerCacheOpenGL::RasterizerCacheOpenGL() {