renderer_vulkan: Implement partial color/depth clears
This commit is contained in:
@ -94,6 +94,10 @@ TextureRuntime::~TextureRuntime() {
|
|||||||
device.destroyImageView(alloc.image_view);
|
device.destroyImageView(alloc.image_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& [key, framebuffer] : clear_framebuffers) {
|
||||||
|
device.destroyFramebuffer(framebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
texture_recycler.clear();
|
texture_recycler.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,49 +225,84 @@ void TextureRuntime::FormatConvert(VideoCore::PixelFormat format, bool upload,
|
|||||||
|
|
||||||
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
|
bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
|
||||||
VideoCore::ClearValue value) {
|
VideoCore::ClearValue value) {
|
||||||
|
const vk::ImageAspectFlags aspect = ToVkAspect(surface.type);
|
||||||
renderpass_cache.ExitRenderpass();
|
renderpass_cache.ExitRenderpass();
|
||||||
|
|
||||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferDstOptimal,
|
Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferDstOptimal,
|
||||||
0, surface.alloc.levels, 0, surface.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
0, surface.alloc.levels, 0, surface.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
|
||||||
|
|
||||||
|
vk::ClearValue clear_value{};
|
||||||
|
if (aspect & vk::ImageAspectFlagBits::eColor) {
|
||||||
|
clear_value.color = vk::ClearColorValue{
|
||||||
|
.float32 = std::to_array({value.color[0], value.color[1], value.color[2], value.color[3]})
|
||||||
|
};
|
||||||
|
} else if (aspect & vk::ImageAspectFlagBits::eDepth || aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||||
|
clear_value.depthStencil = vk::ClearDepthStencilValue{
|
||||||
|
.depth = value.depth,
|
||||||
|
.stencil = value.stencil
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// For full clears we can use vkCmdClearColorImage/vkCmdClearDepthStencilImage
|
// For full clears we can use vkCmdClearColorImage/vkCmdClearDepthStencilImage
|
||||||
if (clear.texture_rect == surface.GetScaledRect()) {
|
if (clear.texture_rect == surface.GetScaledRect()) {
|
||||||
vk::ImageAspectFlags aspect = ToVkAspect(surface.type);
|
const vk::ImageSubresourceRange range = {
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.baseMipLevel = clear.texture_level,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1
|
||||||
|
};
|
||||||
|
|
||||||
if (aspect & vk::ImageAspectFlagBits::eColor) {
|
if (aspect & vk::ImageAspectFlagBits::eColor) {
|
||||||
const vk::ClearColorValue clear_color = {
|
|
||||||
.float32 = std::to_array({value.color[0], value.color[1], value.color[2], value.color[3]})
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::ImageSubresourceRange range = {
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.baseMipLevel = clear.texture_level,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
command_buffer.clearColorImage(surface.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
command_buffer.clearColorImage(surface.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
||||||
clear_color, range);
|
clear_value.color, range);
|
||||||
} else if (aspect & vk::ImageAspectFlagBits::eDepth || aspect & vk::ImageAspectFlagBits::eStencil) {
|
} else if (aspect & vk::ImageAspectFlagBits::eDepth || aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||||
const vk::ClearDepthStencilValue clear_depth = {
|
|
||||||
.depth = value.depth,
|
|
||||||
.stencil = value.stencil
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::ImageSubresourceRange range = {
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.baseMipLevel = clear.texture_level,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
command_buffer.clearDepthStencilImage(surface.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
command_buffer.clearDepthStencilImage(surface.alloc.image, vk::ImageLayout::eTransferDstOptimal,
|
||||||
clear_depth, range);
|
clear_value.depthStencil, range);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING(Render_Vulkan, "Partial clears are unimplemented!");
|
// For partial clears we begin a clear renderpass with the appropriate render area
|
||||||
|
vk::RenderPass clear_renderpass{};
|
||||||
|
ImageAlloc& alloc = surface.alloc;
|
||||||
|
if (aspect & vk::ImageAspectFlagBits::eColor) {
|
||||||
|
clear_renderpass = renderpass_cache.GetRenderpass(surface.pixel_format,
|
||||||
|
VideoCore::PixelFormat::Invalid, true);
|
||||||
|
Transition(command_buffer, alloc, vk::ImageLayout::eColorAttachmentOptimal, 0, alloc.levels);
|
||||||
|
} else if (aspect & vk::ImageAspectFlagBits::eDepth || aspect & vk::ImageAspectFlagBits::eStencil) {
|
||||||
|
clear_renderpass = renderpass_cache.GetRenderpass(VideoCore::PixelFormat::Invalid,
|
||||||
|
surface.pixel_format, true);
|
||||||
|
Transition(command_buffer, alloc, vk::ImageLayout::eDepthStencilAttachmentOptimal, 0, alloc.levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [it, new_framebuffer] = clear_framebuffers.try_emplace(alloc.image_view, vk::Framebuffer{});
|
||||||
|
if (new_framebuffer) {
|
||||||
|
const vk::FramebufferCreateInfo framebuffer_info = {
|
||||||
|
.renderPass = clear_renderpass,
|
||||||
|
.attachmentCount = 1,
|
||||||
|
.pAttachments = &alloc.image_view,
|
||||||
|
.width = surface.GetScaledWidth(),
|
||||||
|
.height = surface.GetScaledHeight(),
|
||||||
|
.layers = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::Device device = instance.GetDevice();
|
||||||
|
it->second = device.createFramebuffer(framebuffer_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const vk::RenderPassBeginInfo clear_begin_info = {
|
||||||
|
.renderPass = clear_renderpass,
|
||||||
|
.framebuffer = it->second,
|
||||||
|
.renderArea = vk::Rect2D{
|
||||||
|
.offset = {static_cast<s32>(clear.texture_rect.left), static_cast<s32>(clear.texture_rect.bottom)},
|
||||||
|
.extent = {clear.texture_rect.GetWidth(), clear.texture_rect.GetHeight()}
|
||||||
|
},
|
||||||
|
.clearValueCount = 1,
|
||||||
|
.pClearValues = &clear_value
|
||||||
|
};
|
||||||
|
|
||||||
|
renderpass_cache.EnterRenderpass(clear_begin_info);
|
||||||
|
renderpass_cache.ExitRenderpass();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <vulkan/vulkan_hash.hpp>
|
||||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||||
#include "video_core/rasterizer_cache/surface_base.h"
|
#include "video_core/rasterizer_cache/surface_base.h"
|
||||||
#include "video_core/rasterizer_cache/types.h"
|
#include "video_core/rasterizer_cache/types.h"
|
||||||
@ -97,6 +98,7 @@ private:
|
|||||||
std::array<std::unique_ptr<StagingBuffer>, SCHEDULER_COMMAND_COUNT> staging_buffers;
|
std::array<std::unique_ptr<StagingBuffer>, SCHEDULER_COMMAND_COUNT> staging_buffers;
|
||||||
std::array<u32, SCHEDULER_COMMAND_COUNT> staging_offsets{};
|
std::array<u32, SCHEDULER_COMMAND_COUNT> staging_offsets{};
|
||||||
std::unordered_multimap<VideoCore::HostTextureTag, ImageAlloc> texture_recycler;
|
std::unordered_multimap<VideoCore::HostTextureTag, ImageAlloc> texture_recycler;
|
||||||
|
std::unordered_map<vk::ImageView, vk::Framebuffer> clear_framebuffers;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Surface : public VideoCore::SurfaceBase<Surface> {
|
class Surface : public VideoCore::SurfaceBase<Surface> {
|
||||||
|
Reference in New Issue
Block a user