feat: Add OpenGL ZBC clear support and refactor ZBC management

Moved ZBCManager to `video_core/zbc_manager.cpp/h` for modularity. Added
`gl_zbc_clear.cpp/h` for efficient OpenGL color, depth, and stencil clears.
Updated `RasterizerOpenGL::Clear` to use ZBC with fallback. Added stencil
type validation. Fixed color mask and logging. Updated `CMakeLists.txt`.
Enhances performance and code organization.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-09-14 20:28:39 +10:00
parent 12c3e4b92c
commit 94119302ec
11 changed files with 769 additions and 54 deletions

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/renderer_vulkan/vk_zbc_clear.h"
#include "common/settings.h"
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
@@ -592,6 +593,10 @@ void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
const std::array<f32, 4>& clear_color,
const Region2D& dst_region) {
// Try to use ZBC (Zero Bandwidth Clear) for efficient clearing
// Note: We need the format and type from the context, but for now we'll use fallback
// TODO: Integrate with proper format/type detection from the rendering context
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,

View File

@@ -35,7 +35,7 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
switch (framebuffer.pixel_format) {
case Service::android::PixelFormat::Rgba8888:
case Service::android::PixelFormat::Rgbx8888:
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
return VK_FORMAT_R8G8B8A8_UNORM;
case Service::android::PixelFormat::Rgb565:
return VK_FORMAT_R5G6B5_UNORM_PACK16;
case Service::android::PixelFormat::Bgra8888:
@@ -43,7 +43,7 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
default:
UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
static_cast<u32>(framebuffer.pixel_format));
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
return VK_FORMAT_R8G8B8A8_UNORM;
}
}

View File

@@ -0,0 +1,172 @@
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/renderer_vulkan/vk_zbc_clear.h"
#include "common/logging/log.h"
#include "video_core/zbc_manager.h"
namespace Vulkan {
bool ZBCClear::ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout layout,
u32 format, u32 type) {
const auto zbc_color = VideoCore::ZBCManager::Instance().GetZBCColor(format, type);
if (!zbc_color) {
LOG_TRACE(Render_Vulkan, "ZBC: No color entry found for format=0x{:X}, type=0x{:X}, using fallback", format, type);
return false;
}
const VkClearColorValue clear_color = ConvertColorToVulkan(*zbc_color);
LOG_TRACE(Render_Vulkan, "ZBC: Using ZBC clear color for format=0x{:X}, type=0x{:X}", format, type);
LOG_TRACE(Render_Vulkan, "ZBC: Clear color=[{:.3f}, {:.3f}, {:.3f}, {:.3f}]",
clear_color.float32[0], clear_color.float32[1], clear_color.float32[2], clear_color.float32[3]);
static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
}}};
cmdbuf.ClearColorImage(image, layout, clear_color, subresources);
return true;
}
bool ZBCClear::ClearDepthStencilImage(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout layout,
u32 format, u32 type, u32 stencil_value) {
const auto zbc_depth = VideoCore::ZBCManager::Instance().GetZBCDepth(format, type);
if (!zbc_depth) {
LOG_TRACE(Render_Vulkan, "ZBC: No depth entry found for format=0x{:X}, type=0x{:X}, using fallback", format, type);
return false;
}
const f32 clear_depth = VideoCore::ZBCManager::ConvertDepthToFloat(*zbc_depth);
LOG_TRACE(Render_Vulkan, "ZBC: Using ZBC clear depth-stencil for format=0x{:X}, type=0x{:X}", format, type);
LOG_TRACE(Render_Vulkan, "ZBC: Clear depth={:.6f}, stencil={}", clear_depth, stencil_value);
const VkClearDepthStencilValue clear_value{
.depth = clear_depth,
.stencil = stencil_value,
};
// Use ClearAttachments for depth-stencil clearing
const VkClearValue clear_value_union{
.depthStencil = clear_value,
};
const VkClearAttachment attachment{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.colorAttachment = 0,
.clearValue = clear_value_union,
};
const VkClearRect clear_rect{
.rect = {{0, 0}, {0xFFFFFFFF, 0xFFFFFFFF}}, // Clear entire image
.baseArrayLayer = 0,
.layerCount = 1,
};
cmdbuf.ClearAttachments({attachment}, {clear_rect});
return true;
}
std::optional<VkClearColorValue> ZBCClear::GetZBCClearColor(u32 format, u32 type) {
const auto zbc_color = VideoCore::ZBCManager::Instance().GetZBCColor(format, type);
if (!zbc_color) {
return std::nullopt;
}
return ConvertColorToVulkan(*zbc_color);
}
std::optional<f32> ZBCClear::GetZBCClearDepth(u32 format, u32 type) {
const auto zbc_depth = VideoCore::ZBCManager::Instance().GetZBCDepth(format, type);
if (!zbc_depth) {
return std::nullopt;
}
return VideoCore::ZBCManager::ConvertDepthToFloat(*zbc_depth);
}
VkClearColorValue ZBCClear::ConvertColorToVulkan(const std::array<u32, 4>& color_u32) {
// Convert ZBC color values to Vulkan format
// The ZBC color values are typically in RGBA8888 format
VkClearColorValue clear_color{};
// For now, we'll use the first color value and convert it to RGBA
// This might need adjustment based on the actual format used by the game
const u32 primary_color = color_u32[0];
// Extract RGBA components (assuming RGBA8888 format)
const u8 r = (primary_color >> 0) & 0xFF;
const u8 g = (primary_color >> 8) & 0xFF;
const u8 b = (primary_color >> 16) & 0xFF;
const u8 a = (primary_color >> 24) & 0xFF;
// Convert to normalized float values [0.0, 1.0]
clear_color.float32[0] = static_cast<f32>(r) / 255.0f; // Red
clear_color.float32[1] = static_cast<f32>(g) / 255.0f; // Green
clear_color.float32[2] = static_cast<f32>(b) / 255.0f; // Blue
clear_color.float32[3] = static_cast<f32>(a) / 255.0f; // Alpha
// If the color is all zeros, use a default clear color
if (clear_color.float32[0] == 0.0f && clear_color.float32[1] == 0.0f &&
clear_color.float32[2] == 0.0f && clear_color.float32[3] == 0.0f) {
// Use a default clear color (black with full alpha)
clear_color.float32[0] = 0.0f; // Red
clear_color.float32[1] = 0.0f; // Green
clear_color.float32[2] = 0.0f; // Blue
clear_color.float32[3] = 1.0f; // Alpha
}
return clear_color;
}
bool ZBCClear::ClearStencilImage(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout layout,
u32 format, u32 type, u32 stencil_value) {
// For stencil clearing, we need to check if we have a ZBC entry for stencil type (2)
const auto zbc_color = VideoCore::ZBCManager::Instance().GetZBCColor(format, 2);
if (!zbc_color) {
LOG_TRACE(Render_Vulkan, "ZBC: No stencil entry found for format=0x{:X}, type=0x{:X}, using fallback", format, type);
return false;
}
// Convert ZBC color to stencil value
// For stencil, we typically use the first component as the stencil value
const u32 zbc_stencil = zbc_color.value()[0];
// Convert to the appropriate stencil format
const u32 clear_stencil = zbc_stencil & 0xFF; // Use lower 8 bits for stencil
LOG_TRACE(Render_Vulkan, "ZBC: Using stencil clear value 0x{:X} for format=0x{:X}", clear_stencil, format);
// Create clear value for stencil
const VkClearDepthStencilValue clear_value{
.depth = 1.0f, // Default depth value
.stencil = clear_stencil,
};
// Use ClearAttachments for stencil clearing
const VkClearValue clear_value_union{
.depthStencil = clear_value,
};
const VkClearAttachment attachment{
.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT,
.colorAttachment = 0,
.clearValue = clear_value_union,
};
const VkClearRect clear_rect{
.rect = {{0, 0}, {0xFFFFFFFF, 0xFFFFFFFF}}, // Clear entire image
.baseArrayLayer = 0,
.layerCount = 1,
};
cmdbuf.ClearAttachments({attachment}, {clear_rect});
return true;
}
} // namespace Vulkan

View File

@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <optional>
#include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "video_core/zbc_manager.h"
namespace Vulkan {
/**
* ZBC-aware buffer clearing for Vulkan renderer
*
* This class provides efficient buffer clearing operations using ZBC (Zero Bandwidth Clear)
* table entries when available, falling back to standard clear operations when needed.
*/
class ZBCClear {
public:
/**
* Clear color image using ZBC if available
* @param cmdbuf Vulkan command buffer
* @param image Image to clear
* @param layout Current image layout
* @param format Surface format identifier
* @param type Clear type (0=color, 1=depth)
* @return True if ZBC clear was used, false if fallback is needed
*/
static bool ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout layout,
u32 format, u32 type);
/**
* Clear depth-stencil image using ZBC if available
* @param cmdbuf Vulkan command buffer
* @param image Image to clear
* @param layout Current image layout
* @param format Surface format identifier
* @param type Clear type (0=color, 1=depth, 2=stencil)
* @param stencil_value Stencil clear value
* @return True if ZBC clear was used, false if fallback is needed
*/
static bool ClearDepthStencilImage(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout layout,
u32 format, u32 type, u32 stencil_value);
/**
* Clear stencil image using ZBC if available
* @param cmdbuf Vulkan command buffer
* @param image Image to clear
* @param layout Current image layout
* @param format Surface format identifier
* @param type Clear type (0=color, 1=depth, 2=stencil)
* @param stencil_value Stencil clear value
* @return True if ZBC clear was used, false if fallback is needed
*/
static bool ClearStencilImage(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout layout,
u32 format, u32 type, u32 stencil_value);
/**
* Get ZBC clear color for a specific format and type
* @param format Surface format identifier
* @param type Clear type (0=color, 1=depth)
* @return Optional VkClearColorValue, or nullopt if not available
*/
static std::optional<VkClearColorValue> GetZBCClearColor(u32 format, u32 type);
/**
* Get ZBC clear depth for a specific format and type
* @param format Surface format identifier
* @param type Clear type (0=color, 1=depth)
* @return Optional depth value, or nullopt if not available
*/
static std::optional<f32> GetZBCClearDepth(u32 format, u32 type);
private:
/**
* Convert ZBC color values to Vulkan format
* @param color_u32 Array of 4 uint32 color values
* @return VkClearColorValue for Vulkan
*/
static VkClearColorValue ConvertColorToVulkan(const std::array<u32, 4>& color_u32);
};
} // namespace Vulkan