Merge pull request #1580 from FernandoS27/mm-impl

Implemented Mipmaps
This commit is contained in:
bunnei 2018-10-29 22:34:00 -04:00 committed by GitHub
commit c5a849212f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 255 additions and 110 deletions

View file

@ -731,11 +731,15 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
if (mag_filter != config.mag_filter) { if (mag_filter != config.mag_filter) {
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; 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) { if (wrap_u != config.wrap_u) {

View file

@ -93,6 +93,7 @@ private:
private: private:
Tegra::Texture::TextureFilter mag_filter; Tegra::Texture::TextureFilter mag_filter;
Tegra::Texture::TextureFilter min_filter; Tegra::Texture::TextureFilter min_filter;
Tegra::Texture::TextureMipmapFilter mip_filter;
Tegra::Texture::WrapMode wrap_u; Tegra::Texture::WrapMode wrap_u;
Tegra::Texture::WrapMode wrap_v; Tegra::Texture::WrapMode wrap_v;
Tegra::Texture::WrapMode wrap_p; Tegra::Texture::WrapMode wrap_p;

View file

@ -91,27 +91,36 @@ 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 compression_factor{GetCompressionFactor(pixel_format)};
const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
u32 m_depth = (layer_only ? 1U : depth); u32 m_depth = (layer_only ? 1U : depth);
u32 m_width = std::max(1U, width / compression_factor); u32 m_width = MipWidth(mip_level);
u32 m_height = std::max(1U, height / compression_factor); u32 m_height = MipHeight(mip_level);
std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_width = uncompressed ? m_width
m_depth, block_height, block_depth); : std::max(1U, (m_width + compression_factor - 1) / compression_factor);
u32 m_block_height = block_height; m_height = uncompressed
u32 m_block_depth = block_depth; ? m_height
std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size : std::max(1U, (m_height + compression_factor - 1) / compression_factor);
for (u32 i = 1; i < max_mip_level; i++) { m_depth = std::max(1U, m_depth >> mip_level);
m_width = std::max(1U, m_width / 2); u32 m_block_height = MipBlockHeight(mip_level);
m_height = std::max(1U, m_height / 2); u32 m_block_depth = MipBlockDepth(mip_level);
m_depth = std::max(1U, m_depth / 2); return Tegra::Texture::CalculateSize(force_gl ? false : is_tiled, bytes_per_pixel, m_width,
m_block_height = std::max(1U, m_block_height / 2); m_height, m_depth, m_block_height, m_block_depth);
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);
} }
return is_tiled ? Common::AlignUp(size, block_size_bytes) : size;
std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
bool uncompressed) const {
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);
}
if (!force_gl && is_tiled) {
size = Common::AlignUp(size, block_size_bytes);
}
return size;
} }
/*static*/ SurfaceParams SurfaceParams::CreateForTexture( /*static*/ SurfaceParams SurfaceParams::CreateForTexture(
@ -189,7 +198,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
params.unaligned_height = config.height; params.unaligned_height = config.height;
params.target = SurfaceTarget::Texture2D; params.target = SurfaceTarget::Texture2D;
params.depth = 1; params.depth = 1;
params.max_mip_level = 0; params.max_mip_level = 1;
params.is_layered = false; params.is_layered = false;
// Render target specific parameters, not used for caching // Render target specific parameters, not used for caching
@ -223,7 +232,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
params.unaligned_height = zeta_height; params.unaligned_height = zeta_height;
params.target = SurfaceTarget::Texture2D; params.target = SurfaceTarget::Texture2D;
params.depth = 1; params.depth = 1;
params.max_mip_level = 0; params.max_mip_level = 1;
params.is_layered = false; params.is_layered = false;
params.rt = {}; params.rt = {};
@ -250,7 +259,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
params.unaligned_height = config.height; params.unaligned_height = config.height;
params.target = SurfaceTarget::Texture2D; params.target = SurfaceTarget::Texture2D;
params.depth = 1; params.depth = 1;
params.max_mip_level = 0; params.max_mip_level = 1;
params.rt = {}; params.rt = {};
params.InitCacheParameters(config.Address()); params.InitCacheParameters(config.Address());
@ -374,13 +383,13 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
return format; return format;
} }
MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const {
u32 actual_height{unaligned_height}; u32 actual_height{std::max(1U, unaligned_height >> mip_level)};
if (IsPixelFormatASTC(pixel_format)) { if (IsPixelFormatASTC(pixel_format)) {
// ASTC formats must stop at the ATSC block size boundary // ASTC formats must stop at the ATSC block size boundary
actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second); 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 /// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
@ -564,28 +573,31 @@ static constexpr GLConversionArray gl_to_morton_fns = {
}; };
void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
std::vector<u8>& gl_buffer) { std::vector<u8>& gl_buffer, u32 mip_level) {
u32 depth = params.depth; u32 depth = params.MipDepth(mip_level);
if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
// TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
depth = 1U; depth = 1U;
} }
if (params.is_layered) { if (params.is_layered) {
u64 offset = 0; u64 offset = params.GetMipmapLevelOffset(mip_level);
u64 offset_gl = 0; u64 offset_gl = 0;
u64 layer_size = params.LayerMemorySize(); u64 layer_size = params.LayerMemorySize();
u64 gl_size = params.LayerSizeGL(); u64 gl_size = params.LayerSizeGL(mip_level);
for (u32 i = 0; i < depth; i++) { for (u32 i = 0; i < params.depth; i++) {
functions[static_cast<std::size_t>(params.pixel_format)]( functions[static_cast<std::size_t>(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); gl_buffer.data() + offset_gl, gl_size, params.addr + offset);
offset += layer_size; offset += layer_size;
offset_gl += gl_size; offset_gl += gl_size;
} }
} else { } else {
u64 offset = params.GetMipmapLevelOffset(mip_level);
functions[static_cast<std::size_t>(params.pixel_format)]( functions[static_cast<std::size_t>(params.pixel_format)](
params.width, params.block_height, params.height, params.block_depth, depth, params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
gl_buffer.data(), gl_buffer.size(), params.addr); params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
gl_buffer.size(), params.addr + offset);
} }
} }
@ -840,31 +852,38 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
// Only pre-create the texture for non-compressed textures. // Only pre-create the texture for non-compressed textures.
switch (params.target) { switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture1D: case SurfaceParams::SurfaceTarget::Texture1D:
glTexStorage1D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level,
rect.GetWidth()); format_tuple.internal_format, rect.GetWidth());
break; break;
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceParams::SurfaceTarget::Texture2D:
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceParams::SurfaceTarget::TextureCubemap:
glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level,
rect.GetWidth(), rect.GetHeight()); format_tuple.internal_format, rect.GetWidth(), rect.GetHeight());
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceParams::SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceParams::SurfaceTarget::Texture2DArray:
glTexStorage3D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
rect.GetWidth(), rect.GetHeight(), params.depth); format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
params.depth);
break; break;
default: default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
static_cast<u32>(params.target)); static_cast<u32>(params.target));
UNREACHABLE(); UNREACHABLE();
glTexStorage2D(GL_TEXTURE_2D, 1, format_tuple.internal_format, rect.GetWidth(), glTexStorage2D(GL_TEXTURE_2D, params.max_mip_level, format_tuple.internal_format,
rect.GetHeight()); rect.GetWidth(), rect.GetHeight());
} }
} }
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); 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_S, GL_CLAMP_TO_EDGE);
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, 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);
}
LabelGLObject(GL_TEXTURE, texture.handle, params.addr, LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
SurfaceParams::SurfaceTargetName(params.target)); SurfaceParams::SurfaceTargetName(params.target));
@ -993,20 +1012,22 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
void CachedSurface::LoadGLBuffer() { void CachedSurface::LoadGLBuffer() {
MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
gl_buffer.resize(params.max_mip_level);
gl_buffer.resize(params.size_in_bytes_gl); for (u32 i = 0; i < params.max_mip_level; i++)
gl_buffer[i].resize(params.GetMipmapSizeGL(i));
if (params.is_tiled) { if (params.is_tiled) {
ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
params.block_width, static_cast<u32>(params.target)); params.block_width, static_cast<u32>(params.target));
for (u32 i = 0; i < params.max_mip_level; i++)
SwizzleFunc(morton_to_gl_fns, params, gl_buffer); SwizzleFunc(morton_to_gl_fns, params, gl_buffer[i], i);
} else { } else {
const auto texture_src_data{Memory::GetPointer(params.addr)}; const auto texture_src_data{Memory::GetPointer(params.addr)};
const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; 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);
} }
for (u32 i = 0; i < params.max_mip_level; i++)
ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); 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)); MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
@ -1016,7 +1037,8 @@ void CachedSurface::FlushGLBuffer() {
ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented"); ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
// OpenGL temporary buffer needs to be big enough to store raw texture size // 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); const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
@ -1025,9 +1047,9 @@ void CachedSurface::FlushGLBuffer() {
ASSERT(!tuple.compressed); ASSERT(!tuple.compressed);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, glGetTextureImage(texture.handle, 0, tuple.format, tuple.type,
static_cast<GLsizei>(gl_buffer.size()), gl_buffer.data()); static_cast<GLsizei>(gl_buffer[0].size()), gl_buffer[0].data());
glPixelStorei(GL_PACK_ROW_LENGTH, 0); 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); params.height);
ASSERT(params.type != SurfaceType::Fill); ASSERT(params.type != SurfaceType::Fill);
const u8* const texture_src_data = Memory::GetPointer(params.addr); const u8* const texture_src_data = Memory::GetPointer(params.addr);
@ -1036,26 +1058,21 @@ void CachedSurface::FlushGLBuffer() {
ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
params.block_width, static_cast<u32>(params.target)); params.block_width, static_cast<u32>(params.target));
SwizzleFunc(gl_to_morton_fns, params, gl_buffer); SwizzleFunc(gl_to_morton_fns, params, gl_buffer[0], 0);
} else { } 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::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { GLuint draw_fb_handle) {
if (params.type == SurfaceType::Fill) const auto& rect{params.GetRect(mip_map)};
return;
MICROPROFILE_SCOPE(OpenGL_TextureUL);
const auto& rect{params.GetRect()};
// Load data from memory to the surface // Load data from memory to the surface
const GLint x0 = static_cast<GLint>(rect.left); const GLint x0 = static_cast<GLint>(rect.left);
const GLint y0 = static_cast<GLint>(rect.bottom); const GLint y0 = static_cast<GLint>(rect.bottom);
std::size_t buffer_offset = std::size_t buffer_offset =
static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) +
static_cast<std::size_t>(x0)) * static_cast<std::size_t>(x0)) *
SurfaceParams::GetBytesPerPixel(params.pixel_format); SurfaceParams::GetBytesPerPixel(params.pixel_format);
@ -1073,88 +1090,117 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
cur_state.Apply(); cur_state.Apply();
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); ASSERT(params.MipWidth(mip_map) * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 ==
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map)));
GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
if (tuple.compressed) { if (tuple.compressed) {
switch (params.target) { switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceParams::SurfaceTarget::Texture2D:
glCompressedTexImage2D( glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
SurfaceTargetToGL(params.target), 0, tuple.internal_format, static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size,
static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceParams::SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray: glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
glCompressedTexImage3D( static_cast<GLsizei>(params.MipWidth(mip_map)),
SurfaceTargetToGL(params.target), 0, tuple.internal_format, static_cast<GLsizei>(params.MipHeight(mip_map)),
static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size,
static_cast<GLsizei>(params.depth), 0, &gl_buffer[mip_map][buffer_offset]);
static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceParams::SurfaceTarget::Texture2DArray:
glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.MipHeight(mip_map)),
static_cast<GLsizei>(params.depth), 0, image_size,
&gl_buffer[mip_map][buffer_offset]);
break;
case SurfaceParams::SurfaceTarget::TextureCubemap: {
GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map));
for (std::size_t face = 0; face < params.depth; ++face) { for (std::size_t face = 0; face < params.depth; ++face) {
glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
0, tuple.internal_format, static_cast<GLsizei>(params.width), mip_map, tuple.internal_format,
static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()), static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
&gl_buffer[buffer_offset]); layer_size, &gl_buffer[mip_map][buffer_offset]);
buffer_offset += params.SizeInBytesCubeFace(); buffer_offset += layer_size;
} }
break; break;
}
default: default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
static_cast<u32>(params.target)); static_cast<u32>(params.target));
UNREACHABLE(); UNREACHABLE();
glCompressedTexImage2D( glCompressedTexImage2D(GL_TEXTURE_2D, mip_map, tuple.internal_format,
GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); static_cast<GLsizei>(params.size_in_bytes_gl),
&gl_buffer[mip_map][buffer_offset]);
} }
} else { } else {
switch (params.target) { switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture1D: case SurfaceParams::SurfaceTarget::Texture1D:
glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0, glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0,
static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture2D: case SurfaceParams::SurfaceTarget::Texture2D:
glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0, glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0,
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceParams::SurfaceTarget::Texture3D: case SurfaceParams::SurfaceTarget::Texture3D:
glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map),
tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
break;
case SurfaceParams::SurfaceTarget::Texture2DArray: case SurfaceParams::SurfaceTarget::Texture2DArray:
glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0, glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
tuple.type, &gl_buffer[buffer_offset]); tuple.type, &gl_buffer[mip_map][buffer_offset]);
break; 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) { for (std::size_t face = 0; face < params.depth; ++face) {
glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, x0, glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map,
y0, static_cast<GLsizei>(rect.GetWidth()), x0, y0, static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
buffer_offset += params.SizeInBytesCubeFace(); buffer_offset += params.LayerSizeGL(mip_map);
} }
break; break;
}
default: default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
static_cast<u32>(params.target)); static_cast<u32>(params.target));
UNREACHABLE(); UNREACHABLE();
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), glTexSubImage2D(GL_TEXTURE_2D, mip_map, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]); &gl_buffer[mip_map][buffer_offset]);
} }
} }
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 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);
}
RasterizerCacheOpenGL::RasterizerCacheOpenGL() { RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
read_framebuffer.Create(); read_framebuffer.Create();
draw_framebuffer.Create(); draw_framebuffer.Create();

View file

@ -834,7 +834,7 @@ struct SurfaceParams {
} }
/// Returns the rectangle corresponding to this surface /// Returns the rectangle corresponding to this surface
MathUtil::Rectangle<u32> GetRect() const; MathUtil::Rectangle<u32> GetRect(u32 mip_level = 0) const;
/// Returns the total size of this surface in bytes, adjusted for compression /// Returns the total size of this surface in bytes, adjusted for compression
std::size_t SizeInBytesRaw(bool ignore_tiled = false) const { 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. /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps.
std::size_t MemorySize() const { std::size_t MemorySize() const {
std::size_t size = InnerMemorySize(is_layered); std::size_t size = InnerMemorySize(false, is_layered);
if (is_layered) if (is_layered)
return size * depth; return size * depth;
return size; return size;
@ -874,12 +874,78 @@ struct SurfaceParams {
/// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including
/// mipmaps. /// mipmaps.
std::size_t LayerMemorySize() const { std::size_t LayerMemorySize() const {
return InnerMemorySize(true); return InnerMemorySize(false, true);
} }
/// Returns the size of a layer of this surface in OpenGL. /// Returns the size of a layer of this surface in OpenGL.
std::size_t LayerSizeGL() const { std::size_t LayerSizeGL(u32 mip_level) const {
return SizeInBytesRaw(true) / depth; 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);
}
// 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) const {
if (mip_level == 0)
return block_height;
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;
}
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;
while (bd > 1 && depth * 2 <= bd) {
bd >>= 1;
}
if (bd == 32) {
u32 bh = MipBlockHeight(mip_level);
if (bh >= 4)
return 16;
}
return bd;
} }
/// Creates SurfaceParams from a texture configuration /// Creates SurfaceParams from a texture configuration
@ -940,7 +1006,10 @@ struct SurfaceParams {
} rt; } rt;
private: 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 }; // namespace OpenGL
@ -1002,8 +1071,10 @@ public:
void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
private: private:
void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle);
OGLTexture texture; OGLTexture texture;
std::vector<u8> gl_buffer; std::vector<std::vector<u8>> gl_buffer;
SurfaceParams params; SurfaceParams params;
GLenum gl_target; GLenum gl_target;
std::size_t cached_size_in_bytes; std::size_t cached_size_in_bytes;

View file

@ -135,12 +135,29 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
return {}; 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) { switch (filter_mode) {
case Tegra::Texture::TextureFilter::Linear: case Tegra::Texture::TextureFilter::Linear: {
switch (mip_filter_mode) {
case Tegra::Texture::TextureMipmapFilter::None:
return GL_LINEAR; return GL_LINEAR;
case Tegra::Texture::TextureFilter::Nearest: 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; 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={}", LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}",
static_cast<u32>(filter_mode)); static_cast<u32>(filter_mode));

View file

@ -10,6 +10,12 @@
namespace Tegra::Texture { 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. * Unswizzles a swizzled texture without changing its format.
*/ */