|
|
@ -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), |
|
|
|
|
|
SurfaceParams::MaxPixelFormat> |
|
|
|
|
|
morton_to_gl_fns = { |
|
|
|
|
|
// clang-format off
|
|
|
|
|
|
|
|
|
using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), |
|
|
|
|
|
SurfaceParams::MaxPixelFormat>; |
|
|
|
|
|
|
|
|
|
|
|
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), |
|
|
|
|
|
SurfaceParams::MaxPixelFormat> |
|
|
|
|
|
gl_to_morton_fns = { |
|
|
|
|
|
// clang-format off
|
|
|
|
|
|
|
|
|
static constexpr GLConversionArray 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) { |
|
|
|
|
|
// 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); |
|
|
|
|
|
|
|
|
SwizzleFunc(morton_to_gl_fns, params, gl_buffer); |
|
|
} 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) { |
|
|
|
|
|
// 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()); |
|
|
|
|
|
|
|
|
SwizzleFunc(gl_to_morton_fns, params, gl_buffer); |
|
|
} 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); |
|
|
|