mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-12-23 17:00:57 +01:00
Fixed Layered Textures Loading and Cubemaps
This commit is contained in:
parent
f034121620
commit
e0ea2f5f6e
3 changed files with 109 additions and 72 deletions
|
@ -78,6 +78,29 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t SurfaceParams::InnerMemorySize(bool layer_only) 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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return is_tiled ? Common::AlignUp(size, block_size_bytes) : size;
|
||||||
|
}
|
||||||
|
|
||||||
/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
|
/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
|
||||||
const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
|
const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
|
||||||
SurfaceParams params{};
|
SurfaceParams params{};
|
||||||
|
@ -124,6 +147,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.is_layered = SurfaceTargetIsLayered(params.target);
|
||||||
params.max_mip_level = config.tic.max_mip_level + 1;
|
params.max_mip_level = config.tic.max_mip_level + 1;
|
||||||
params.rt = {};
|
params.rt = {};
|
||||||
|
|
||||||
|
@ -150,6 +174,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
|
||||||
params.target = SurfaceTarget::Texture2D;
|
params.target = SurfaceTarget::Texture2D;
|
||||||
params.depth = 1;
|
params.depth = 1;
|
||||||
params.max_mip_level = 0;
|
params.max_mip_level = 0;
|
||||||
|
params.is_layered = false;
|
||||||
|
|
||||||
// Render target specific parameters, not used for caching
|
// Render target specific parameters, not used for caching
|
||||||
params.rt.index = static_cast<u32>(index);
|
params.rt.index = static_cast<u32>(index);
|
||||||
|
@ -182,6 +207,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
|
||||||
params.target = SurfaceTarget::Texture2D;
|
params.target = SurfaceTarget::Texture2D;
|
||||||
params.depth = 1;
|
params.depth = 1;
|
||||||
params.max_mip_level = 0;
|
params.max_mip_level = 0;
|
||||||
|
params.is_layered = false;
|
||||||
params.rt = {};
|
params.rt = {};
|
||||||
|
|
||||||
params.InitCacheParameters(zeta_address);
|
params.InitCacheParameters(zeta_address);
|
||||||
|
@ -361,10 +387,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
|
using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
|
||||||
SurfaceParams::MaxPixelFormat>
|
SurfaceParams::MaxPixelFormat>;
|
||||||
morton_to_gl_fns = {
|
|
||||||
// clang-format off
|
static constexpr GLConversionArray morton_to_gl_fns = {
|
||||||
|
// clang-format off
|
||||||
MortonCopy<true, PixelFormat::ABGR8U>,
|
MortonCopy<true, PixelFormat::ABGR8U>,
|
||||||
MortonCopy<true, PixelFormat::ABGR8S>,
|
MortonCopy<true, PixelFormat::ABGR8S>,
|
||||||
MortonCopy<true, PixelFormat::ABGR8UI>,
|
MortonCopy<true, PixelFormat::ABGR8UI>,
|
||||||
|
@ -418,13 +445,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
|
||||||
MortonCopy<true, PixelFormat::Z24S8>,
|
MortonCopy<true, PixelFormat::Z24S8>,
|
||||||
MortonCopy<true, PixelFormat::S8Z24>,
|
MortonCopy<true, PixelFormat::S8Z24>,
|
||||||
MortonCopy<true, PixelFormat::Z32FS8>,
|
MortonCopy<true, PixelFormat::Z32FS8>,
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
|
static constexpr GLConversionArray gl_to_morton_fns = {
|
||||||
SurfaceParams::MaxPixelFormat>
|
// clang-format off
|
||||||
gl_to_morton_fns = {
|
|
||||||
// clang-format off
|
|
||||||
MortonCopy<false, PixelFormat::ABGR8U>,
|
MortonCopy<false, PixelFormat::ABGR8U>,
|
||||||
MortonCopy<false, PixelFormat::ABGR8S>,
|
MortonCopy<false, PixelFormat::ABGR8S>,
|
||||||
MortonCopy<false, PixelFormat::ABGR8UI>,
|
MortonCopy<false, PixelFormat::ABGR8UI>,
|
||||||
|
@ -479,9 +504,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
|
||||||
MortonCopy<false, PixelFormat::Z24S8>,
|
MortonCopy<false, PixelFormat::Z24S8>,
|
||||||
MortonCopy<false, PixelFormat::S8Z24>,
|
MortonCopy<false, PixelFormat::S8Z24>,
|
||||||
MortonCopy<false, PixelFormat::Z32FS8>,
|
MortonCopy<false, PixelFormat::Z32FS8>,
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
|
||||||
|
std::vector<u8>& gl_buffer) {
|
||||||
|
u32 depth = params.depth;
|
||||||
|
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_gl = 0;
|
||||||
|
u64 layer_size = params.LayerMemorySize();
|
||||||
|
u64 gl_size = params.LayerSizeGL();
|
||||||
|
for (u32 i = 0; i < depth; i++) {
|
||||||
|
functions[static_cast<std::size_t>(params.pixel_format)](
|
||||||
|
params.width, params.block_height, params.height, params.block_depth, 1,
|
||||||
|
gl_buffer.data() + offset_gl, gl_size, params.addr + offset);
|
||||||
|
offset += layer_size;
|
||||||
|
offset_gl += gl_size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
functions[static_cast<std::size_t>(params.pixel_format)](
|
||||||
|
params.width, params.block_height, params.height, params.block_depth, depth,
|
||||||
|
gl_buffer.data(), gl_buffer.size(), params.addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
|
static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
|
||||||
GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
|
GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
|
||||||
GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
|
GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
|
||||||
|
@ -881,21 +932,10 @@ void CachedSurface::LoadGLBuffer() {
|
||||||
|
|
||||||
gl_buffer.resize(params.size_in_bytes_gl);
|
gl_buffer.resize(params.size_in_bytes_gl);
|
||||||
if (params.is_tiled) {
|
if (params.is_tiled) {
|
||||||
u32 depth = params.depth;
|
|
||||||
u32 block_depth = params.block_depth;
|
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
|
SwizzleFunc(morton_to_gl_fns, params, gl_buffer);
|
||||||
// TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
|
|
||||||
depth = 1U;
|
|
||||||
block_depth = 1U;
|
|
||||||
}
|
|
||||||
|
|
||||||
morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
|
|
||||||
params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
|
|
||||||
gl_buffer.size(), params.addr);
|
|
||||||
} 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};
|
||||||
|
@ -929,19 +969,10 @@ void CachedSurface::FlushGLBuffer() {
|
||||||
const u8* const texture_src_data = Memory::GetPointer(params.addr);
|
const u8* const texture_src_data = Memory::GetPointer(params.addr);
|
||||||
ASSERT(texture_src_data);
|
ASSERT(texture_src_data);
|
||||||
if (params.is_tiled) {
|
if (params.is_tiled) {
|
||||||
u32 depth = params.depth;
|
|
||||||
u32 block_depth = params.block_depth;
|
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
|
SwizzleFunc(gl_to_morton_fns, params, gl_buffer);
|
||||||
// TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
|
|
||||||
depth = 1U;
|
|
||||||
}
|
|
||||||
gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
|
|
||||||
params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
|
|
||||||
gl_buffer.size(), GetAddr());
|
|
||||||
} else {
|
} else {
|
||||||
std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
|
std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
|
||||||
}
|
}
|
||||||
|
@ -1179,7 +1210,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
|
||||||
const Surface& dst_surface) {
|
const Surface& dst_surface) {
|
||||||
const auto& src_params{src_surface->GetSurfaceParams()};
|
const auto& src_params{src_surface->GetSurfaceParams()};
|
||||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||||
FlushRegion(src_params.addr, dst_params.size_in_bytes);
|
FlushRegion(src_params.addr, dst_params.MemorySize());
|
||||||
LoadSurface(dst_surface);
|
LoadSurface(dst_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1221,44 +1252,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||||
CopySurface(old_surface, new_surface, copy_pbo.handle);
|
CopySurface(old_surface, new_surface, copy_pbo.handle);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SurfaceParams::SurfaceTarget::TextureCubemap:
|
||||||
case SurfaceParams::SurfaceTarget::Texture3D:
|
case SurfaceParams::SurfaceTarget::Texture3D:
|
||||||
AccurateCopySurface(old_surface, new_surface);
|
AccurateCopySurface(old_surface, new_surface);
|
||||||
break;
|
break;
|
||||||
case SurfaceParams::SurfaceTarget::TextureCubemap: {
|
|
||||||
if (old_params.rt.array_mode != 1) {
|
|
||||||
// TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
|
|
||||||
// yet (array rendering used as a cubemap texture).
|
|
||||||
LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode);
|
|
||||||
UNREACHABLE();
|
|
||||||
return new_surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This seems to be used for render-to-cubemap texture
|
|
||||||
ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected");
|
|
||||||
ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected");
|
|
||||||
ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented");
|
|
||||||
|
|
||||||
// TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels.
|
|
||||||
// Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild.
|
|
||||||
const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)};
|
|
||||||
|
|
||||||
for (std::size_t index = 0; index < new_params.depth; ++index) {
|
|
||||||
Surface face_surface{TryGetReservedSurface(old_params)};
|
|
||||||
ASSERT_MSG(face_surface, "Unexpected");
|
|
||||||
|
|
||||||
if (is_blit) {
|
|
||||||
BlitSurface(face_surface, new_surface, read_framebuffer.handle,
|
|
||||||
draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index,
|
|
||||||
new_params.rt.index, index);
|
|
||||||
} else {
|
|
||||||
CopySurface(face_surface, new_surface, copy_pbo.handle,
|
|
||||||
face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
old_params.addr += byte_stride;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
||||||
static_cast<u32>(new_params.target));
|
static_cast<u32>(new_params.target));
|
||||||
|
@ -1266,7 +1263,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_surface;
|
return new_surface;
|
||||||
}
|
} // namespace OpenGL
|
||||||
|
|
||||||
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const {
|
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const {
|
||||||
return TryGet(addr);
|
return TryGet(addr);
|
||||||
|
|
|
@ -168,6 +168,23 @@ struct SurfaceParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool SurfaceTargetIsLayered(SurfaceTarget target) {
|
||||||
|
switch (target) {
|
||||||
|
case SurfaceTarget::Texture1D:
|
||||||
|
case SurfaceTarget::Texture2D:
|
||||||
|
case SurfaceTarget::Texture3D:
|
||||||
|
return false;
|
||||||
|
case SurfaceTarget::Texture1DArray:
|
||||||
|
case SurfaceTarget::Texture2DArray:
|
||||||
|
case SurfaceTarget::TextureCubemap:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
|
||||||
|
UNREACHABLE();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the compression factor for the specified PixelFormat. This applies to just the
|
* Gets the compression factor for the specified PixelFormat. This applies to just the
|
||||||
* "compressed width" and "compressed height", not the overall compression factor of a
|
* "compressed width" and "compressed height", not the overall compression factor of a
|
||||||
|
@ -742,6 +759,25 @@ struct SurfaceParams {
|
||||||
return size_in_bytes_gl / 6;
|
return size_in_bytes_gl / 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
if (is_layered)
|
||||||
|
return size * depth;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the size of a layer of this surface in OpenGL.
|
||||||
|
std::size_t LayerSizeGL() const {
|
||||||
|
return SizeInBytesRaw(true) / depth;
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates SurfaceParams from a texture configuration
|
/// Creates SurfaceParams from a texture configuration
|
||||||
static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config,
|
static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config,
|
||||||
const GLShader::SamplerEntry& entry);
|
const GLShader::SamplerEntry& entry);
|
||||||
|
@ -782,6 +818,7 @@ struct SurfaceParams {
|
||||||
u32 unaligned_height;
|
u32 unaligned_height;
|
||||||
SurfaceTarget target;
|
SurfaceTarget target;
|
||||||
u32 max_mip_level;
|
u32 max_mip_level;
|
||||||
|
bool is_layered;
|
||||||
|
|
||||||
// Parameters used for caching
|
// Parameters used for caching
|
||||||
VAddr addr;
|
VAddr addr;
|
||||||
|
@ -797,6 +834,9 @@ struct SurfaceParams {
|
||||||
u32 layer_stride;
|
u32 layer_stride;
|
||||||
u32 base_layer;
|
u32 base_layer;
|
||||||
} rt;
|
} rt;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t InnerMemorySize(bool layer_only = false) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace OpenGL
|
}; // namespace OpenGL
|
||||||
|
|
|
@ -320,13 +320,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||||
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
||||||
u32 block_height, u32 block_depth) {
|
u32 block_height, u32 block_depth) {
|
||||||
if (tiled) {
|
if (tiled) {
|
||||||
const u32 gobs_in_x = 64 / bytes_per_pixel;
|
const u32 gobs_in_x = 64;
|
||||||
const u32 gobs_in_y = 8;
|
const u32 gobs_in_y = 8;
|
||||||
const u32 gobs_in_z = 1;
|
const u32 gobs_in_z = 1;
|
||||||
const u32 aligned_width = Common::AlignUp(width, gobs_in_x);
|
const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
|
||||||
const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
|
const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
|
||||||
const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
|
const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
|
||||||
return aligned_width * aligned_height * aligned_depth * bytes_per_pixel;
|
return aligned_width * aligned_height * aligned_depth;
|
||||||
} else {
|
} else {
|
||||||
return width * height * depth * bytes_per_pixel;
|
return width * height * depth * bytes_per_pixel;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue