182 lines
6.6 KiB
C++
182 lines
6.6 KiB
C++
// Copyright 2023 Citra Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include "common/alignment.h"
|
|
#include "video_core/custom_textures/material.h"
|
|
#include "video_core/rasterizer_cache/surface_base.h"
|
|
#include "video_core/texture/texture_decode.h"
|
|
|
|
namespace VideoCore {
|
|
|
|
SurfaceBase::SurfaceBase(const SurfaceParams& params) : SurfaceParams{params} {}
|
|
|
|
SurfaceBase::~SurfaceBase() = default;
|
|
|
|
bool SurfaceBase::CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const {
|
|
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
|
|
boost::icl::first(fill_interval) >= addr &&
|
|
boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
|
|
dest_surface.FromInterval(fill_interval).GetInterval() ==
|
|
fill_interval) { // make sure interval is a rectangle in dest surface
|
|
if (fill_size * 8 != dest_surface.GetFormatBpp()) {
|
|
// Check if bits repeat for our fill_size
|
|
const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / 8, 1u);
|
|
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
|
|
|
|
for (u32 i = 0; i < dest_bytes_per_pixel; ++i) {
|
|
std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
|
|
}
|
|
|
|
for (u32 i = 0; i < fill_size; ++i) {
|
|
if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0],
|
|
dest_bytes_per_pixel) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SurfaceBase::CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const {
|
|
const SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval);
|
|
ASSERT(subrect_params.GetInterval() == copy_interval);
|
|
|
|
if (CanSubRect(subrect_params)) {
|
|
return true;
|
|
}
|
|
|
|
if (CanFill(dest_surface, copy_interval)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
SurfaceInterval SurfaceBase::GetCopyableInterval(const SurfaceParams& params) const {
|
|
SurfaceInterval result{};
|
|
const u32 tile_align = params.BytesInPixels(params.is_tiled ? 8 * 8 : 1);
|
|
const auto valid_regions =
|
|
SurfaceRegions{params.GetInterval() & GetInterval()} - invalid_regions;
|
|
|
|
for (auto& valid_interval : valid_regions) {
|
|
const SurfaceInterval aligned_interval{
|
|
params.addr +
|
|
Common::AlignUp(boost::icl::first(valid_interval) - params.addr, tile_align),
|
|
params.addr +
|
|
Common::AlignDown(boost::icl::last_next(valid_interval) - params.addr, tile_align)};
|
|
|
|
if (tile_align > boost::icl::length(valid_interval) ||
|
|
boost::icl::length(aligned_interval) == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Get the rectangle within aligned_interval
|
|
const u32 stride_bytes = params.BytesInPixels(params.stride) * (params.is_tiled ? 8 : 1);
|
|
SurfaceInterval rect_interval{
|
|
params.addr +
|
|
Common::AlignUp(boost::icl::first(aligned_interval) - params.addr, stride_bytes),
|
|
params.addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - params.addr,
|
|
stride_bytes),
|
|
};
|
|
|
|
if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
|
|
// 1 row
|
|
rect_interval = aligned_interval;
|
|
} else if (boost::icl::length(rect_interval) == 0) {
|
|
// 2 rows that do not make a rectangle, return the larger one
|
|
const SurfaceInterval row1{boost::icl::first(aligned_interval),
|
|
boost::icl::first(rect_interval)};
|
|
const SurfaceInterval row2{boost::icl::first(rect_interval),
|
|
boost::icl::last_next(aligned_interval)};
|
|
rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2;
|
|
}
|
|
|
|
if (boost::icl::length(rect_interval) > boost::icl::length(result)) {
|
|
result = rect_interval;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Extent SurfaceBase::RealExtent(bool scaled) const {
|
|
const bool is_custom = IsCustom();
|
|
u32 real_width = width;
|
|
u32 real_height = height;
|
|
if (is_custom) {
|
|
real_width = material->width;
|
|
real_height = material->height;
|
|
} else if (scaled) {
|
|
real_width = GetScaledWidth();
|
|
real_height = GetScaledHeight();
|
|
}
|
|
return Extent{
|
|
.width = real_width,
|
|
.height = real_height,
|
|
};
|
|
}
|
|
|
|
bool SurfaceBase::HasNormalMap() const noexcept {
|
|
return material && material->Map(MapType::Normal) != nullptr;
|
|
}
|
|
|
|
ClearValue SurfaceBase::MakeClearValue(PAddr copy_addr, PixelFormat dst_format) {
|
|
const SurfaceType dst_type = GetFormatType(dst_format);
|
|
const std::array fill_buffer = MakeFillBuffer(copy_addr);
|
|
|
|
ClearValue result{};
|
|
switch (dst_type) {
|
|
case SurfaceType::Color:
|
|
case SurfaceType::Texture:
|
|
case SurfaceType::Fill: {
|
|
Pica::Texture::TextureInfo tex_info{};
|
|
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(dst_format);
|
|
const auto color = Pica::Texture::LookupTexture(fill_buffer.data(), 0, 0, tex_info);
|
|
result.color = color / 255.f;
|
|
break;
|
|
}
|
|
case SurfaceType::Depth: {
|
|
u32 depth_uint = 0;
|
|
if (dst_format == PixelFormat::D16) {
|
|
std::memcpy(&depth_uint, fill_buffer.data(), 2);
|
|
result.depth = depth_uint / 65535.0f; // 2^16 - 1
|
|
} else if (dst_format == PixelFormat::D24) {
|
|
std::memcpy(&depth_uint, fill_buffer.data(), 3);
|
|
result.depth = depth_uint / 16777215.0f; // 2^24 - 1
|
|
}
|
|
break;
|
|
}
|
|
case SurfaceType::DepthStencil: {
|
|
u32 clear_value_uint;
|
|
std::memcpy(&clear_value_uint, fill_buffer.data(), sizeof(u32));
|
|
result.depth = (clear_value_uint & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
|
|
result.stencil = (clear_value_uint >> 24);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE_MSG("Invalid surface type!");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::array<u8, 4> SurfaceBase::MakeFillBuffer(PAddr copy_addr) {
|
|
const PAddr fill_offset = (copy_addr - addr) % fill_size;
|
|
std::array<u8, 4> fill_buffer;
|
|
|
|
u32 fill_buff_pos = fill_offset;
|
|
for (std::size_t i = 0; i < fill_buffer.size(); i++) {
|
|
fill_buffer[i] = fill_data[fill_buff_pos++ % fill_size];
|
|
}
|
|
|
|
return fill_buffer;
|
|
}
|
|
|
|
} // namespace VideoCore
|