Browse Source
Merge pull request #3248 from ReinUsesLisp/vk-image
Merge pull request #3248 from ReinUsesLisp/vk-image
vk_image: Add an image object abstractionpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 192 additions and 0 deletions
-
2src/video_core/CMakeLists.txt
-
106src/video_core/renderer_vulkan/vk_image.cpp
-
84src/video_core/renderer_vulkan/vk_image.h
@ -0,0 +1,106 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <memory>
|
|||
#include <vector>
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "video_core/renderer_vulkan/declarations.h"
|
|||
#include "video_core/renderer_vulkan/vk_device.h"
|
|||
#include "video_core/renderer_vulkan/vk_image.h"
|
|||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
|||
|
|||
namespace Vulkan { |
|||
|
|||
VKImage::VKImage(const VKDevice& device, VKScheduler& scheduler, |
|||
const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask) |
|||
: device{device}, scheduler{scheduler}, format{image_ci.format}, aspect_mask{aspect_mask}, |
|||
image_num_layers{image_ci.arrayLayers}, image_num_levels{image_ci.mipLevels} { |
|||
UNIMPLEMENTED_IF_MSG(image_ci.queueFamilyIndexCount != 0, |
|||
"Queue family tracking is not implemented"); |
|||
|
|||
const auto dev = device.GetLogical(); |
|||
image = dev.createImageUnique(image_ci, nullptr, device.GetDispatchLoader()); |
|||
|
|||
const u32 num_ranges = image_num_layers * image_num_levels; |
|||
barriers.resize(num_ranges); |
|||
subrange_states.resize(num_ranges, {{}, image_ci.initialLayout}); |
|||
} |
|||
|
|||
VKImage::~VKImage() = default; |
|||
|
|||
void VKImage::Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, |
|||
vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access, |
|||
vk::ImageLayout new_layout) { |
|||
if (!HasChanged(base_layer, num_layers, base_level, num_levels, new_access, new_layout)) { |
|||
return; |
|||
} |
|||
|
|||
std::size_t cursor = 0; |
|||
for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) { |
|||
for (u32 level_it = 0; level_it < num_levels; ++level_it, ++cursor) { |
|||
const u32 layer = base_layer + layer_it; |
|||
const u32 level = base_level + level_it; |
|||
auto& state = GetSubrangeState(layer, level); |
|||
barriers[cursor] = vk::ImageMemoryBarrier( |
|||
state.access, new_access, state.layout, new_layout, VK_QUEUE_FAMILY_IGNORED, |
|||
VK_QUEUE_FAMILY_IGNORED, *image, {aspect_mask, level, 1, layer, 1}); |
|||
state.access = new_access; |
|||
state.layout = new_layout; |
|||
} |
|||
} |
|||
|
|||
scheduler.RequestOutsideRenderPassOperationContext(); |
|||
|
|||
scheduler.Record([barriers = barriers, cursor](auto cmdbuf, auto& dld) { |
|||
// TODO(Rodrigo): Implement a way to use the latest stage across subresources.
|
|||
constexpr auto stage_stub = vk::PipelineStageFlagBits::eAllCommands; |
|||
cmdbuf.pipelineBarrier(stage_stub, stage_stub, {}, 0, nullptr, 0, nullptr, |
|||
static_cast<u32>(cursor), barriers.data(), dld); |
|||
}); |
|||
} |
|||
|
|||
bool VKImage::HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, |
|||
vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept { |
|||
const bool is_full_range = base_layer == 0 && num_layers == image_num_layers && |
|||
base_level == 0 && num_levels == image_num_levels; |
|||
if (!is_full_range) { |
|||
state_diverged = true; |
|||
} |
|||
|
|||
if (!state_diverged) { |
|||
auto& state = GetSubrangeState(0, 0); |
|||
if (state.access != new_access || state.layout != new_layout) { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) { |
|||
for (u32 level_it = 0; level_it < num_levels; ++level_it) { |
|||
const u32 layer = base_layer + layer_it; |
|||
const u32 level = base_level + level_it; |
|||
auto& state = GetSubrangeState(layer, level); |
|||
if (state.access != new_access || state.layout != new_layout) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
void VKImage::CreatePresentView() { |
|||
// Image type has to be 2D to be presented.
|
|||
const vk::ImageViewCreateInfo image_view_ci({}, *image, vk::ImageViewType::e2D, format, {}, |
|||
{aspect_mask, 0, 1, 0, 1}); |
|||
const auto dev = device.GetLogical(); |
|||
const auto& dld = device.GetDispatchLoader(); |
|||
present_view = dev.createImageViewUnique(image_view_ci, nullptr, dld); |
|||
} |
|||
|
|||
VKImage::SubrangeState& VKImage::GetSubrangeState(u32 layer, u32 level) noexcept { |
|||
return subrange_states[static_cast<std::size_t>(layer * image_num_levels) + |
|||
static_cast<std::size_t>(level)]; |
|||
} |
|||
|
|||
} // namespace Vulkan
|
|||
@ -0,0 +1,84 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <vector> |
|||
|
|||
#include "common/common_types.h" |
|||
#include "video_core/renderer_vulkan/declarations.h" |
|||
|
|||
namespace Vulkan { |
|||
|
|||
class VKDevice; |
|||
class VKScheduler; |
|||
|
|||
class VKImage { |
|||
public: |
|||
explicit VKImage(const VKDevice& device, VKScheduler& scheduler, |
|||
const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask); |
|||
~VKImage(); |
|||
|
|||
/// Records in the passed command buffer an image transition and updates the state of the image. |
|||
void Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, |
|||
vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access, |
|||
vk::ImageLayout new_layout); |
|||
|
|||
/// Returns a view compatible with presentation, the image has to be 2D. |
|||
vk::ImageView GetPresentView() { |
|||
if (!present_view) { |
|||
CreatePresentView(); |
|||
} |
|||
return *present_view; |
|||
} |
|||
|
|||
/// Returns the Vulkan image handler. |
|||
vk::Image GetHandle() const { |
|||
return *image; |
|||
} |
|||
|
|||
/// Returns the Vulkan format for this image. |
|||
vk::Format GetFormat() const { |
|||
return format; |
|||
} |
|||
|
|||
/// Returns the Vulkan aspect mask. |
|||
vk::ImageAspectFlags GetAspectMask() const { |
|||
return aspect_mask; |
|||
} |
|||
|
|||
private: |
|||
struct SubrangeState final { |
|||
vk::AccessFlags access{}; ///< Current access bits. |
|||
vk::ImageLayout layout = vk::ImageLayout::eUndefined; ///< Current image layout. |
|||
}; |
|||
|
|||
bool HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, |
|||
vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept; |
|||
|
|||
/// Creates a presentation view. |
|||
void CreatePresentView(); |
|||
|
|||
/// Returns the subrange state for a layer and layer. |
|||
SubrangeState& GetSubrangeState(u32 layer, u32 level) noexcept; |
|||
|
|||
const VKDevice& device; ///< Device handler. |
|||
VKScheduler& scheduler; ///< Device scheduler. |
|||
|
|||
const vk::Format format; ///< Vulkan format. |
|||
const vk::ImageAspectFlags aspect_mask; ///< Vulkan aspect mask. |
|||
const u32 image_num_layers; ///< Number of layers. |
|||
const u32 image_num_levels; ///< Number of mipmap levels. |
|||
|
|||
UniqueImage image; ///< Image handle. |
|||
UniqueImageView present_view; ///< Image view compatible with presentation. |
|||
|
|||
std::vector<vk::ImageMemoryBarrier> barriers; ///< Pool of barriers. |
|||
std::vector<SubrangeState> subrange_states; ///< Current subrange state. |
|||
|
|||
bool state_diverged = false; ///< True when subresources mismatch in layout. |
|||
}; |
|||
|
|||
} // namespace Vulkan |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue