mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-04-04 10:30:12 -04:00
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:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
172
src/video_core/renderer_vulkan/vk_zbc_clear.cpp
Normal file
172
src/video_core/renderer_vulkan/vk_zbc_clear.cpp
Normal 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
|
||||
86
src/video_core/renderer_vulkan/vk_zbc_clear.h
Normal file
86
src/video_core/renderer_vulkan/vk_zbc_clear.h
Normal 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
|
||||
Reference in New Issue
Block a user