mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 08:59:03 -06:00 
			
		
		
		
	Merge pull request #3425 from ReinUsesLisp/layered-framebuffer
texture_cache: Implement layered framebuffer attachments
This commit is contained in:
		@@ -542,7 +542,7 @@ public:
 | 
			
		||||
                BitField<12, 1, InvMemoryLayout> type;
 | 
			
		||||
            } memory_layout;
 | 
			
		||||
            union {
 | 
			
		||||
                BitField<0, 16, u32> array_mode;
 | 
			
		||||
                BitField<0, 16, u32> layers;
 | 
			
		||||
                BitField<16, 1, u32> volume;
 | 
			
		||||
            };
 | 
			
		||||
            u32 layer_stride;
 | 
			
		||||
@@ -800,8 +800,12 @@ public:
 | 
			
		||||
 | 
			
		||||
                u32 zeta_width;
 | 
			
		||||
                u32 zeta_height;
 | 
			
		||||
                union {
 | 
			
		||||
                    BitField<0, 16, u32> zeta_layers;
 | 
			
		||||
                    BitField<16, 1, u32> zeta_volume;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                INSERT_UNION_PADDING_WORDS(0x27);
 | 
			
		||||
                INSERT_UNION_PADDING_WORDS(0x26);
 | 
			
		||||
 | 
			
		||||
                u32 depth_test_enable;
 | 
			
		||||
 | 
			
		||||
@@ -1507,6 +1511,7 @@ ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
 | 
			
		||||
ASSERT_REG_POSITION(rt_control, 0x487);
 | 
			
		||||
ASSERT_REG_POSITION(zeta_width, 0x48a);
 | 
			
		||||
ASSERT_REG_POSITION(zeta_height, 0x48b);
 | 
			
		||||
ASSERT_REG_POSITION(zeta_layers, 0x48c);
 | 
			
		||||
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
 | 
			
		||||
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
 | 
			
		||||
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
 | 
			
		||||
 
 | 
			
		||||
@@ -405,24 +405,36 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
 | 
			
		||||
CachedSurfaceView::~CachedSurfaceView() = default;
 | 
			
		||||
 | 
			
		||||
void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const {
 | 
			
		||||
    ASSERT(params.num_layers == 1 && params.num_levels == 1);
 | 
			
		||||
    ASSERT(params.num_levels == 1);
 | 
			
		||||
 | 
			
		||||
    const auto& owner_params = surface.GetSurfaceParams();
 | 
			
		||||
    const GLuint texture = surface.GetTexture();
 | 
			
		||||
    if (params.num_layers > 1) {
 | 
			
		||||
        // Layered framebuffer attachments
 | 
			
		||||
        UNIMPLEMENTED_IF(params.base_layer != 0);
 | 
			
		||||
 | 
			
		||||
    switch (owner_params.target) {
 | 
			
		||||
        switch (params.target) {
 | 
			
		||||
        case SurfaceTarget::Texture2DArray:
 | 
			
		||||
            glFramebufferTexture(target, attachment, texture, params.base_level);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            UNIMPLEMENTED();
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const GLenum view_target = surface.GetTarget();
 | 
			
		||||
    switch (surface.GetSurfaceParams().target) {
 | 
			
		||||
    case SurfaceTarget::Texture1D:
 | 
			
		||||
        glFramebufferTexture1D(target, attachment, surface.GetTarget(), surface.GetTexture(),
 | 
			
		||||
                               params.base_level);
 | 
			
		||||
        glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level);
 | 
			
		||||
        break;
 | 
			
		||||
    case SurfaceTarget::Texture2D:
 | 
			
		||||
        glFramebufferTexture2D(target, attachment, surface.GetTarget(), surface.GetTexture(),
 | 
			
		||||
                               params.base_level);
 | 
			
		||||
        glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level);
 | 
			
		||||
        break;
 | 
			
		||||
    case SurfaceTarget::Texture1DArray:
 | 
			
		||||
    case SurfaceTarget::Texture2DArray:
 | 
			
		||||
    case SurfaceTarget::TextureCubemap:
 | 
			
		||||
    case SurfaceTarget::TextureCubeArray:
 | 
			
		||||
        glFramebufferTextureLayer(target, attachment, surface.GetTexture(), params.base_level,
 | 
			
		||||
        glFramebufferTextureLayer(target, attachment, texture, params.base_level,
 | 
			
		||||
                                  params.base_layer);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
 
 | 
			
		||||
@@ -611,33 +611,34 @@ bool RasterizerVulkan::WalkAttachmentOverlaps(const CachedSurfaceView& attachmen
 | 
			
		||||
std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers(
 | 
			
		||||
    vk::RenderPass renderpass) {
 | 
			
		||||
    FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(),
 | 
			
		||||
                            std::numeric_limits<u32>::max()};
 | 
			
		||||
                            std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
 | 
			
		||||
 | 
			
		||||
    const auto MarkAsModifiedAndPush = [&](const View& view) {
 | 
			
		||||
        if (view == nullptr) {
 | 
			
		||||
    const auto try_push = [&](const View& view) {
 | 
			
		||||
        if (!view) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        key.views.push_back(view->GetHandle());
 | 
			
		||||
        key.width = std::min(key.width, view->GetWidth());
 | 
			
		||||
        key.height = std::min(key.height, view->GetHeight());
 | 
			
		||||
        key.layers = std::min(key.layers, view->GetNumLayers());
 | 
			
		||||
        return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (std::size_t index = 0; index < std::size(color_attachments); ++index) {
 | 
			
		||||
        if (MarkAsModifiedAndPush(color_attachments[index])) {
 | 
			
		||||
        if (try_push(color_attachments[index])) {
 | 
			
		||||
            texture_cache.MarkColorBufferInUse(index);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (MarkAsModifiedAndPush(zeta_attachment)) {
 | 
			
		||||
    if (try_push(zeta_attachment)) {
 | 
			
		||||
        texture_cache.MarkDepthBufferInUse();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key);
 | 
			
		||||
    auto& framebuffer = fbentry->second;
 | 
			
		||||
    if (is_cache_miss) {
 | 
			
		||||
        const vk::FramebufferCreateInfo framebuffer_ci({}, key.renderpass,
 | 
			
		||||
                                                       static_cast<u32>(key.views.size()),
 | 
			
		||||
                                                       key.views.data(), key.width, key.height, 1);
 | 
			
		||||
        const vk::FramebufferCreateInfo framebuffer_ci(
 | 
			
		||||
            {}, key.renderpass, static_cast<u32>(key.views.size()), key.views.data(), key.width,
 | 
			
		||||
            key.height, key.layers);
 | 
			
		||||
        const auto dev = device.GetLogical();
 | 
			
		||||
        const auto& dld = device.GetDispatchLoader();
 | 
			
		||||
        framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld);
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,7 @@ struct FramebufferCacheKey {
 | 
			
		||||
    vk::RenderPass renderpass{};
 | 
			
		||||
    u32 width = 0;
 | 
			
		||||
    u32 height = 0;
 | 
			
		||||
    u32 layers = 0;
 | 
			
		||||
    ImageViewsPack views;
 | 
			
		||||
 | 
			
		||||
    std::size_t Hash() const noexcept {
 | 
			
		||||
@@ -66,12 +67,17 @@ struct FramebufferCacheKey {
 | 
			
		||||
        }
 | 
			
		||||
        boost::hash_combine(hash, width);
 | 
			
		||||
        boost::hash_combine(hash, height);
 | 
			
		||||
        boost::hash_combine(hash, layers);
 | 
			
		||||
        return hash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool operator==(const FramebufferCacheKey& rhs) const noexcept {
 | 
			
		||||
        return std::tie(renderpass, views, width, height) ==
 | 
			
		||||
               std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height);
 | 
			
		||||
        return std::tie(renderpass, views, width, height, layers) ==
 | 
			
		||||
               std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height, rhs.layers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool operator!=(const FramebufferCacheKey& rhs) const noexcept {
 | 
			
		||||
        return !operator==(rhs);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -151,6 +151,10 @@ public:
 | 
			
		||||
        return params.GetMipHeight(base_level);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 GetNumLayers() const {
 | 
			
		||||
        return num_layers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsBufferView() const {
 | 
			
		||||
        return buffer_view;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -84,19 +84,16 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta
 | 
			
		||||
    if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) {
 | 
			
		||||
        switch (params.pixel_format) {
 | 
			
		||||
        case PixelFormat::R16U:
 | 
			
		||||
        case PixelFormat::R16F: {
 | 
			
		||||
        case PixelFormat::R16F:
 | 
			
		||||
            params.pixel_format = PixelFormat::Z16;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case PixelFormat::R32F: {
 | 
			
		||||
        case PixelFormat::R32F:
 | 
			
		||||
            params.pixel_format = PixelFormat::Z32F;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        default: {
 | 
			
		||||
        default:
 | 
			
		||||
            UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}",
 | 
			
		||||
                              static_cast<u32>(params.pixel_format));
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
        params.type = GetFormatType(params.pixel_format);
 | 
			
		||||
    }
 | 
			
		||||
    params.type = GetFormatType(params.pixel_format);
 | 
			
		||||
@@ -168,27 +165,29 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl
 | 
			
		||||
    return params;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SurfaceParams SurfaceParams::CreateForDepthBuffer(
 | 
			
		||||
    Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
 | 
			
		||||
    u32 block_width, u32 block_height, u32 block_depth,
 | 
			
		||||
    Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
 | 
			
		||||
SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
 | 
			
		||||
    const auto& regs = system.GPU().Maxwell3D().regs;
 | 
			
		||||
    regs.zeta_width, regs.zeta_height, regs.zeta.format, regs.zeta.memory_layout.type;
 | 
			
		||||
    SurfaceParams params;
 | 
			
		||||
    params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
 | 
			
		||||
    params.is_tiled = regs.zeta.memory_layout.type ==
 | 
			
		||||
                      Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
 | 
			
		||||
    params.srgb_conversion = false;
 | 
			
		||||
    params.block_width = std::min(block_width, 5U);
 | 
			
		||||
    params.block_height = std::min(block_height, 5U);
 | 
			
		||||
    params.block_depth = std::min(block_depth, 5U);
 | 
			
		||||
    params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U);
 | 
			
		||||
    params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U);
 | 
			
		||||
    params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
 | 
			
		||||
    params.tile_width_spacing = 1;
 | 
			
		||||
    params.pixel_format = PixelFormatFromDepthFormat(format);
 | 
			
		||||
    params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
 | 
			
		||||
    params.type = GetFormatType(params.pixel_format);
 | 
			
		||||
    params.width = zeta_width;
 | 
			
		||||
    params.height = zeta_height;
 | 
			
		||||
    params.target = SurfaceTarget::Texture2D;
 | 
			
		||||
    params.depth = 1;
 | 
			
		||||
    params.width = regs.zeta_width;
 | 
			
		||||
    params.height = regs.zeta_height;
 | 
			
		||||
    params.pitch = 0;
 | 
			
		||||
    params.num_levels = 1;
 | 
			
		||||
    params.emulated_levels = 1;
 | 
			
		||||
    params.is_layered = false;
 | 
			
		||||
 | 
			
		||||
    const bool is_layered = regs.zeta_layers > 1 && params.block_depth == 0;
 | 
			
		||||
    params.is_layered = is_layered;
 | 
			
		||||
    params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
 | 
			
		||||
    params.depth = is_layered ? regs.zeta_layers.Value() : 1U;
 | 
			
		||||
    return params;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -214,11 +213,13 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
 | 
			
		||||
        params.width = params.pitch / bpp;
 | 
			
		||||
    }
 | 
			
		||||
    params.height = config.height;
 | 
			
		||||
    params.depth = 1;
 | 
			
		||||
    params.target = SurfaceTarget::Texture2D;
 | 
			
		||||
    params.num_levels = 1;
 | 
			
		||||
    params.emulated_levels = 1;
 | 
			
		||||
    params.is_layered = false;
 | 
			
		||||
 | 
			
		||||
    const bool is_layered = config.layers > 1 && params.block_depth == 0;
 | 
			
		||||
    params.is_layered = is_layered;
 | 
			
		||||
    params.depth = is_layered ? config.layers.Value() : 1;
 | 
			
		||||
    params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
 | 
			
		||||
    return params;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,10 +35,7 @@ public:
 | 
			
		||||
                                        const VideoCommon::Shader::Image& entry);
 | 
			
		||||
 | 
			
		||||
    /// Creates SurfaceCachedParams for a depth buffer configuration.
 | 
			
		||||
    static SurfaceParams CreateForDepthBuffer(
 | 
			
		||||
        Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
 | 
			
		||||
        u32 block_width, u32 block_height, u32 block_depth,
 | 
			
		||||
        Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
 | 
			
		||||
    static SurfaceParams CreateForDepthBuffer(Core::System& system);
 | 
			
		||||
 | 
			
		||||
    /// Creates SurfaceCachedParams from a framebuffer configuration.
 | 
			
		||||
    static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
 | 
			
		||||
 
 | 
			
		||||
@@ -160,10 +160,7 @@ public:
 | 
			
		||||
            SetEmptyDepthBuffer();
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
        const auto depth_params{SurfaceParams::CreateForDepthBuffer(
 | 
			
		||||
            system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
 | 
			
		||||
            regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
 | 
			
		||||
            regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
 | 
			
		||||
        const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)};
 | 
			
		||||
        auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true);
 | 
			
		||||
        if (depth_buffer.target)
 | 
			
		||||
            depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user