mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-10-22 23:00:27 +02:00
Fixed Civilization VII - added TIR mixed samples support
This commit is contained in:
parent
aee3785897
commit
720c7d227c
4 changed files with 185 additions and 7 deletions
|
|
@ -129,7 +129,13 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
|
|||
topology.Assign(can_collapse_topology_class
|
||||
? TOPOLOGY_CLASS_REPRESENTATIVE_LUT[static_cast<size_t>(topology_)]
|
||||
: topology_);
|
||||
msaa_mode.Assign(regs.anti_alias_samples_mode);
|
||||
// Target-independent rasterization rasterizes (and depth tests) at the raster sample count
|
||||
if (regs.tir_mode == Maxwell::TIRMode::RasterNTargetM &&
|
||||
regs.anti_alias_raster != Maxwell::AntiAliasRaster::Mode1x1) {
|
||||
msaa_mode.Assign(static_cast<Tegra::Texture::MsaaMode>(regs.anti_alias_raster));
|
||||
} else {
|
||||
msaa_mode.Assign(regs.anti_alias_samples_mode);
|
||||
}
|
||||
attachment0_dual_source_blend.Assign(ComputeAttachment0DualSourceBlend(regs) ? 1 : 0);
|
||||
|
||||
raw2 = 0;
|
||||
|
|
|
|||
|
|
@ -1089,6 +1089,58 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
|
|||
return *shadow.view;
|
||||
}
|
||||
|
||||
VkImageView TextureCacheRuntime::GetOrCreateTirColorScratch(VkImage color_image, VkFormat format,
|
||||
VkExtent2D extent, u32 layers,
|
||||
VkSampleCountFlagBits samples) {
|
||||
TirColorScratch& scratch = tir_color_scratch[color_image];
|
||||
if (scratch.image && scratch.format == format && scratch.extent.width == extent.width &&
|
||||
scratch.extent.height == extent.height && scratch.layers == layers &&
|
||||
scratch.samples == samples) {
|
||||
return *scratch.view;
|
||||
}
|
||||
if (scratch.image) {
|
||||
scheduler.Finish();
|
||||
}
|
||||
scratch.image = memory_allocator.CreateImage(VkImageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = {extent.width, extent.height, 1},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = layers,
|
||||
.samples = samples,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
});
|
||||
scratch.view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = *scratch.image,
|
||||
.viewType = layers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.components{},
|
||||
.subresourceRange{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = layers,
|
||||
},
|
||||
});
|
||||
scratch.format = format;
|
||||
scratch.extent = extent;
|
||||
scratch.layers = layers;
|
||||
scratch.samples = samples;
|
||||
return *scratch.view;
|
||||
}
|
||||
|
||||
const TextureCacheRuntime::ResolveShadow* TextureCacheRuntime::GetValidResolveShadow(
|
||||
VkImage msaa_image) const {
|
||||
const auto it = resolve_shadows.find(msaa_image);
|
||||
|
|
@ -2970,6 +3022,26 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, ImageView* color_buffer,
|
|||
|
||||
Framebuffer::~Framebuffer() = default;
|
||||
|
||||
/// Single-sampled color targets with a multisampled depth buffer, as used by target-independent
|
||||
/// rasterization. Vulkan requires matching sample counts for these attachments.
|
||||
static bool IsMixedSamplesTarget(std::span<ImageView*, NUM_RT> color_buffers,
|
||||
const ImageView* depth_buffer) {
|
||||
if (depth_buffer == nullptr || depth_buffer->Samples() == VK_SAMPLE_COUNT_1_BIT) {
|
||||
return false;
|
||||
}
|
||||
bool has_color = false;
|
||||
for (const ImageView* const color_buffer : color_buffers) {
|
||||
if (color_buffer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (color_buffer->Samples() != VK_SAMPLE_COUNT_1_BIT) {
|
||||
return false;
|
||||
}
|
||||
has_color = true;
|
||||
}
|
||||
return has_color;
|
||||
}
|
||||
|
||||
void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
std::span<ImageView*, NUM_RT> color_buffers,
|
||||
ImageView* depth_buffer, bool is_rescaled_) {
|
||||
|
|
@ -3024,6 +3096,13 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
|||
} else {
|
||||
renderpass_key.depth_format = PixelFormat::Invalid;
|
||||
}
|
||||
if (IsMixedSamplesTarget(color_buffers, depth_buffer)) {
|
||||
const VkExtent2D extent{(std::min)(render_area.width, width),
|
||||
(std::min)(render_area.height, height)};
|
||||
CreateMixedSamplesFramebuffer(runtime, color_buffers, attachments, renderpass_key,
|
||||
static_cast<u32>((std::max)(num_layers, 1)), extent);
|
||||
return;
|
||||
}
|
||||
renderpass_key.samples = samples;
|
||||
const bool do_resolve_color = ENABLE_MSAA_TILER_RESOLVE &&
|
||||
samples != VK_SAMPLE_COUNT_1_BIT && num_colors > 0 && runtime.device.IsTiler();
|
||||
|
|
@ -3040,10 +3119,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
|||
discard_msaa_depth_stencil = ENABLE_MSAA_RESOLVE_CONSUME &&
|
||||
ENABLE_MSAA_DEPTH_STENCIL_DISCARD && do_resolve_depth_stencil;
|
||||
|
||||
renderpass = runtime.render_pass_cache.Get(renderpass_key);
|
||||
render_pass_key = renderpass_key;
|
||||
render_pass_cache = &runtime.render_pass_cache;
|
||||
runtime_ptr = &runtime;
|
||||
UseRenderPass(runtime, renderpass_key);
|
||||
render_area.width = (std::min)(render_area.width, width);
|
||||
render_area.height = (std::min)(render_area.height, height);
|
||||
|
||||
|
|
@ -3075,6 +3151,53 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
|||
}
|
||||
|
||||
num_color_buffers = static_cast<u32>(num_colors);
|
||||
CreateVkFramebuffer(runtime, attachments, static_cast<u32>((std::max)(num_layers, 1)));
|
||||
}
|
||||
|
||||
void Framebuffer::CreateMixedSamplesFramebuffer(TextureCacheRuntime& runtime,
|
||||
std::span<ImageView*, NUM_RT> color_buffers,
|
||||
std::span<const VkImageView> attachments,
|
||||
RenderPassKey renderpass_key, u32 layers,
|
||||
VkExtent2D extent) {
|
||||
// attachments holds the real color targets followed by the depth buffer
|
||||
const size_t num_colors = attachments.size() - 1;
|
||||
boost::container::small_vector<VkImageView, NUM_RT * 2 + 2> mixed_attachments;
|
||||
for (const ImageView* const color_buffer : color_buffers) {
|
||||
if (!color_buffer) {
|
||||
continue;
|
||||
}
|
||||
const VkFormat vk_format = MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal,
|
||||
true, color_buffer->format)
|
||||
.format;
|
||||
mixed_attachments.push_back(runtime.GetOrCreateTirColorScratch(
|
||||
color_buffer->ImageHandle(), vk_format, extent, layers, samples));
|
||||
}
|
||||
mixed_attachments.push_back(attachments[num_colors]);
|
||||
for (size_t index = 0; index < num_colors; ++index) {
|
||||
mixed_attachments.push_back(attachments[index]);
|
||||
}
|
||||
// Multisampled scratch color + depth, resolving color into the real single-sampled targets
|
||||
renderpass_key.samples = samples;
|
||||
renderpass_key.resolve_color = true;
|
||||
renderpass_key.resolve_depth_stencil = false;
|
||||
discard_msaa_color = false;
|
||||
discard_msaa_depth_stencil = false;
|
||||
UseRenderPass(runtime, renderpass_key);
|
||||
render_area = extent;
|
||||
num_color_buffers = static_cast<u32>(num_colors);
|
||||
CreateVkFramebuffer(runtime, mixed_attachments, layers);
|
||||
}
|
||||
|
||||
void Framebuffer::UseRenderPass(TextureCacheRuntime& runtime,
|
||||
const RenderPassKey& renderpass_key) {
|
||||
renderpass = runtime.render_pass_cache.Get(renderpass_key);
|
||||
render_pass_key = renderpass_key;
|
||||
render_pass_cache = &runtime.render_pass_cache;
|
||||
runtime_ptr = &runtime;
|
||||
}
|
||||
|
||||
void Framebuffer::CreateVkFramebuffer(TextureCacheRuntime& runtime,
|
||||
std::span<const VkImageView> attachments, u32 layers) {
|
||||
framebuffer = runtime.device.GetLogical().CreateFramebuffer({
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
|
@ -3084,7 +3207,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
|||
.pAttachments = attachments.data(),
|
||||
.width = render_area.width,
|
||||
.height = render_area.height,
|
||||
.layers = static_cast<u32>((std::max)(num_layers, 1)),
|
||||
.layers = layers,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,11 @@ public:
|
|||
|
||||
[[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
|
||||
|
||||
/// Multisampled stand-in for a single-sampled color target rendered with a TIR depth buffer
|
||||
[[nodiscard]] VkImageView GetOrCreateTirColorScratch(VkImage color_image, VkFormat format,
|
||||
VkExtent2D extent, u32 layers,
|
||||
VkSampleCountFlagBits samples);
|
||||
|
||||
void InvalidateResolveShadow(VkImage msaa_image);
|
||||
|
||||
void MarkResolveShadowUpToDate(VkImage msaa_image);
|
||||
|
|
@ -187,6 +192,15 @@ public:
|
|||
|
||||
std::vector<MsaaScratchImage> msaa_scratch_images;
|
||||
::Common::unordered_map<VkImage, ResolveShadow> resolve_shadows;
|
||||
struct TirColorScratch {
|
||||
vk::Image image;
|
||||
vk::ImageView view;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent{};
|
||||
u32 layers = 0;
|
||||
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
};
|
||||
::Common::unordered_map<VkImage, TirColorScratch> tir_color_scratch;
|
||||
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
|
||||
};
|
||||
|
||||
|
|
@ -279,6 +293,22 @@ public:
|
|||
void MarkResolveShadowsUpToDate() const;
|
||||
|
||||
private:
|
||||
/// Builds a framebuffer for single-sampled color targets drawn with a multisampled depth
|
||||
/// buffer (target-independent rasterization). Color is rendered into multisampled scratch
|
||||
/// images that resolve into the real targets at the end of the pass.
|
||||
void CreateMixedSamplesFramebuffer(TextureCacheRuntime& runtime,
|
||||
std::span<ImageView*, NUM_RT> color_buffers,
|
||||
std::span<const VkImageView> attachments,
|
||||
RenderPassKey renderpass_key, u32 layers,
|
||||
VkExtent2D extent);
|
||||
|
||||
/// Fetches the render pass for the key from the cache and remembers it for variants.
|
||||
void UseRenderPass(TextureCacheRuntime& runtime, const RenderPassKey& renderpass_key);
|
||||
|
||||
/// Creates the Vulkan framebuffer for the current render pass and render area.
|
||||
void CreateVkFramebuffer(TextureCacheRuntime& runtime,
|
||||
std::span<const VkImageView> attachments, u32 layers);
|
||||
|
||||
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
|
||||
|
||||
vk::Framebuffer framebuffer;
|
||||
|
|
|
|||
|
|
@ -1928,7 +1928,26 @@ ImageViewId TextureCache<P>::FindDepthBuffer() {
|
|||
if (gpu_addr == 0) {
|
||||
return ImageViewId{};
|
||||
}
|
||||
const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode);
|
||||
// Target-independent rasterization: the depth buffer keeps the raster sample count while
|
||||
// the color targets use anti_alias_samples_mode.
|
||||
Tegra::Texture::MsaaMode zeta_msaa_mode = regs.anti_alias_samples_mode;
|
||||
if (regs.anti_alias_raster != Tegra::Engines::Maxwell3D::Regs::AntiAliasRaster::Mode1x1) {
|
||||
const auto raster_mode = static_cast<Tegra::Texture::MsaaMode>(regs.anti_alias_raster);
|
||||
bool use_raster = regs.tir_mode == Tegra::Engines::Maxwell3D::Regs::TIRMode::RasterNTargetM;
|
||||
if (!use_raster && regs.anti_alias_samples_mode == Tegra::Texture::MsaaMode::Msaa1x1) {
|
||||
// Clears can be issued before tir_mode is enabled. Only a depth buffer sized for the
|
||||
// raster samples relative to the color target is treated as multisampled, otherwise
|
||||
// it ping-pongs between a 1x and a multisampled image every frame.
|
||||
const auto& rt0 = regs.rt[0];
|
||||
use_raster = rt0.Address() != 0 && rt0.format != Tegra::RenderTargetFormat::NONE &&
|
||||
regs.zeta_size.width == rt0.width * NumSamplesX(raster_mode) &&
|
||||
regs.zeta_size.height == rt0.height * NumSamplesY(raster_mode);
|
||||
}
|
||||
if (use_raster) {
|
||||
zeta_msaa_mode = raster_mode;
|
||||
}
|
||||
}
|
||||
const ImageInfo info(regs.zeta, regs.zeta_size, zeta_msaa_mode);
|
||||
return FindRenderTargetView(info, gpu_addr);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue