[Vulkan] Frontbuffer presentation

This commit is contained in:
Triang3l
2022-06-25 14:33:43 +03:00
parent 3fc7d8753c
commit 4b4205ba00
9 changed files with 917 additions and 407 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -265,6 +265,9 @@ class VulkanCommandProcessor : public CommandProcessor {
void WriteRegister(uint32_t index, uint32_t value) override;
void OnGammaRamp256EntryTableValueWritten() override;
void OnGammaRampPWLValueWritten() override;
void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height) override;
@@ -398,6 +401,21 @@ class VulkanCommandProcessor : public CommandProcessor {
VkDescriptorSet set;
};
enum SwapApplyGammaDescriptorSet : uint32_t {
kSwapApplyGammaDescriptorSetRamp,
kSwapApplyGammaDescriptorSetSource,
kSwapApplyGammaDescriptorSetCount,
};
// Framebuffer for the current presenter's guest output image revision, and
// its usage tracking.
struct SwapFramebuffer {
VkFramebuffer framebuffer = VK_NULL_HANDLE;
uint64_t version = UINT64_MAX;
uint64_t last_submission = 0;
};
// BeginSubmission and EndSubmission may be called at any time. If there's an
// open non-frame submission, BeginSubmission(true) will promote it to a
// frame. EndSubmission(true) will close the frame no matter whether the
@@ -554,24 +572,55 @@ class VulkanCommandProcessor : public CommandProcessor {
VkDescriptorPool shared_memory_and_edram_descriptor_pool_ = VK_NULL_HANDLE;
VkDescriptorSet shared_memory_and_edram_descriptor_set_;
// Bytes 0x0...0x3FF - 256-entry gamma ramp table with B10G10R10X2 data (read
// as R10G10B10X2 with swizzle).
// Bytes 0x400...0x9FF - 128-entry PWL R16G16 gamma ramp (R - base, G - delta,
// low 6 bits of each are zero, 3 elements per entry).
// kMaxFramesInFlight pairs of gamma ramps if in host-visible memory and
// uploaded directly, one otherwise.
VkDeviceMemory gamma_ramp_buffer_memory_ = VK_NULL_HANDLE;
VkBuffer gamma_ramp_buffer_ = VK_NULL_HANDLE;
// kMaxFramesInFlight pairs, only when the gamma ramp buffer is not
// host-visible.
VkDeviceMemory gamma_ramp_upload_buffer_memory_ = VK_NULL_HANDLE;
VkBuffer gamma_ramp_upload_buffer_ = VK_NULL_HANDLE;
VkDeviceSize gamma_ramp_upload_memory_size_;
uint32_t gamma_ramp_upload_memory_type_;
// Mapping of either gamma_ramp_buffer_memory_ (if it's host-visible) or
// gamma_ramp_upload_buffer_memory_ (otherwise).
void* gamma_ramp_upload_mapping_;
std::array<VkBufferView, 2 * kMaxFramesInFlight> gamma_ramp_buffer_views_{};
// UINT32_MAX if outdated.
uint32_t gamma_ramp_256_entry_table_current_frame_ = UINT32_MAX;
uint32_t gamma_ramp_pwl_current_frame_ = UINT32_MAX;
VkDescriptorSetLayout swap_descriptor_set_layout_sampled_image_ =
VK_NULL_HANDLE;
VkDescriptorSetLayout swap_descriptor_set_layout_uniform_texel_buffer_ =
VK_NULL_HANDLE;
// Descriptor pool for allocating descriptors needed for presentation, such as
// the destination images and the gamma ramps.
VkDescriptorPool swap_descriptor_pool_ = VK_NULL_HANDLE;
// Interleaved 256-entry table and PWL texel buffer descriptors.
// kMaxFramesInFlight pairs of gamma ramps if in host-visible memory and
// uploaded directly, one otherwise.
std::array<VkDescriptorSet, 2 * kMaxFramesInFlight>
swap_descriptors_gamma_ramp_;
// Sampled images.
std::array<VkDescriptorSet, kMaxFramesInFlight> swap_descriptors_source_;
VkPipelineLayout swap_apply_gamma_pipeline_layout_ = VK_NULL_HANDLE;
// Has no dependencies on specific pipeline stages on both ends to simplify
// use in different scenarios with different pipelines - use explicit barriers
// for synchronization. Drawing to VK_FORMAT_R8G8B8A8_SRGB.
VkRenderPass swap_render_pass_ = VK_NULL_HANDLE;
VkPipelineLayout swap_pipeline_layout_ = VK_NULL_HANDLE;
VkPipeline swap_pipeline_ = VK_NULL_HANDLE;
// for synchronization.
VkRenderPass swap_apply_gamma_render_pass_ = VK_NULL_HANDLE;
VkPipeline swap_apply_gamma_256_entry_table_pipeline_ = VK_NULL_HANDLE;
VkPipeline swap_apply_gamma_pwl_pipeline_ = VK_NULL_HANDLE;
// Framebuffer for the current presenter's guest output image revision, and
// its usage tracking.
struct SwapFramebuffer {
VkFramebuffer framebuffer = VK_NULL_HANDLE;
uint64_t version = UINT64_MAX;
uint64_t last_submission = 0;
};
std::array<SwapFramebuffer,
ui::vulkan::VulkanPresenter::kMaxActiveGuestOutputImageVersions>
swap_framebuffers_;
std::deque<std::pair<uint64_t, VkFramebuffer>> swap_framebuffers_outdated_;
// Pending pipeline barriers.
std::vector<VkBufferMemoryBarrier> pending_barriers_buffer_memory_barriers_;

View File

@@ -589,6 +589,59 @@ VkImageView VulkanTextureCache::GetActiveBindingOrNullImageView(
}
}
VkImageView VulkanTextureCache::RequestSwapTexture(
uint32_t& width_scaled_out, uint32_t& height_scaled_out,
xenos::TextureFormat& format_out) {
const auto& regs = register_file();
const auto& fetch = regs.Get<xenos::xe_gpu_texture_fetch_t>(
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0);
TextureKey key;
BindingInfoFromFetchConstant(fetch, key, nullptr);
if (!key.is_valid || key.base_page == 0 ||
key.dimension != xenos::DataDimension::k2DOrStacked) {
return nullptr;
}
VulkanTexture* texture =
static_cast<VulkanTexture*>(FindOrCreateTexture(key));
if (!texture) {
return VK_NULL_HANDLE;
}
VkImageView texture_view = texture->GetView(
false, GuestToHostSwizzle(fetch.swizzle, GetHostFormatSwizzle(key)),
false);
if (texture_view == VK_NULL_HANDLE) {
return VK_NULL_HANDLE;
}
if (!LoadTextureData(*texture)) {
return VK_NULL_HANDLE;
}
texture->MarkAsUsed();
VulkanTexture::Usage old_usage =
texture->SetUsage(VulkanTexture::Usage::kSwapSampled);
if (old_usage != VulkanTexture::Usage::kSwapSampled) {
VkPipelineStageFlags src_stage_mask, dst_stage_mask;
VkAccessFlags src_access_mask, dst_access_mask;
VkImageLayout old_layout, new_layout;
GetTextureUsageMasks(old_usage, src_stage_mask, src_access_mask,
old_layout);
GetTextureUsageMasks(VulkanTexture::Usage::kSwapSampled, dst_stage_mask,
dst_access_mask, new_layout);
command_processor_.PushImageMemoryBarrier(
texture->image(), ui::vulkan::util::InitializeSubresourceRange(),
src_stage_mask, dst_stage_mask, src_access_mask, dst_access_mask,
old_layout, new_layout);
}
// Only texture->key, not the result of BindingInfoFromFetchConstant, contains
// whether the texture is scaled.
key = texture->key();
width_scaled_out =
key.GetWidth() * (key.scaled_resolve ? draw_resolution_scale_x() : 1);
height_scaled_out =
key.GetHeight() * (key.scaled_resolve ? draw_resolution_scale_y() : 1);
format_out = key.format;
return texture_view;
}
bool VulkanTextureCache::IsSignedVersionSeparateForFormat(
TextureKey key) const {
const HostFormatPair& host_format_pair = GetHostFormatPair(key);
@@ -1263,7 +1316,14 @@ VulkanTextureCache::VulkanTexture::~VulkanTexture() {
}
VkImageView VulkanTextureCache::VulkanTexture::GetView(bool is_signed,
uint32_t host_swizzle) {
uint32_t host_swizzle,
bool is_array) {
xenos::DataDimension dimension = key().dimension;
if (dimension == xenos::DataDimension::k3D ||
dimension == xenos::DataDimension::kCube) {
is_array = false;
}
const VulkanTextureCache& vulkan_texture_cache =
static_cast<const VulkanTextureCache&>(texture_cache());
@@ -1297,6 +1357,8 @@ VkImageView VulkanTextureCache::VulkanTexture::GetView(bool is_signed,
}
view_key.host_swizzle = host_swizzle;
view_key.is_array = uint32_t(is_array);
// Try to find an existing view.
auto it = views_.find(view_key);
if (it != views_.end()) {
@@ -1311,17 +1373,6 @@ VkImageView VulkanTextureCache::VulkanTexture::GetView(bool is_signed,
view_create_info.pNext = nullptr;
view_create_info.flags = 0;
view_create_info.image = image();
switch (key().dimension) {
case xenos::DataDimension::k3D:
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D;
break;
case xenos::DataDimension::kCube:
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
break;
default:
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
break;
}
view_create_info.format = format;
view_create_info.components.r = GetComponentSwizzle(host_swizzle, 0);
view_create_info.components.g = GetComponentSwizzle(host_swizzle, 1);
@@ -1329,6 +1380,22 @@ VkImageView VulkanTextureCache::VulkanTexture::GetView(bool is_signed,
view_create_info.components.a = GetComponentSwizzle(host_swizzle, 3);
view_create_info.subresourceRange =
ui::vulkan::util::InitializeSubresourceRange();
switch (dimension) {
case xenos::DataDimension::k3D:
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D;
break;
case xenos::DataDimension::kCube:
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
break;
default:
if (is_array) {
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
} else {
view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_create_info.subresourceRange.layerCount = 1;
}
break;
}
VkImageView view;
if (dfn.vkCreateImageView(device, &view_create_info, nullptr, &view) !=
VK_SUCCESS) {
@@ -2248,9 +2315,10 @@ void VulkanTextureCache::GetTextureUsageMasks(VulkanTexture::Usage usage,
layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
break;
case VulkanTexture::Usage::kSwapSampled:
// The swap texture is likely to be used only for the presentation compute
// shader, and not during emulation, where it'd be used in other stages.
stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
// The swap texture is likely to be used only for the presentation
// fragment shader, and not during emulation, where it'd be used in other
// stages.
stage_mask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
access_mask = VK_ACCESS_SHADER_READ_BIT;
layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
break;

View File

@@ -60,6 +60,13 @@ class VulkanTextureCache final : public TextureCache {
xenos::FetchOpDimension dimension,
bool is_signed) const;
// Returns the 2D view of the front buffer texture (for fragment shader
// reading - the barrier will be pushed in the command processor if needed),
// or VK_NULL_HANDLE in case of failure. May call LoadTextureData.
VkImageView RequestSwapTexture(uint32_t& width_scaled_out,
uint32_t& height_scaled_out,
xenos::TextureFormat& format_out);
protected:
bool IsSignedVersionSeparateForFormat(TextureKey key) const override;
uint32_t GetHostFormatSwizzle(TextureKey key) const override;
@@ -136,7 +143,8 @@ class VulkanTextureCache final : public TextureCache {
return old_usage;
}
VkImageView GetView(bool is_signed, uint32_t host_swizzle);
VkImageView GetView(bool is_signed, uint32_t host_swizzle,
bool is_array = true);
private:
union ViewKey {
@@ -144,6 +152,7 @@ class VulkanTextureCache final : public TextureCache {
struct {
uint32_t is_signed_separate_view : 1;
uint32_t host_swizzle : 12;
uint32_t is_array : 1;
};
ViewKey() : key(0) { static_assert_size(*this, sizeof(key)); }