[Vulkan] Basic framebuffer output
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -14,8 +14,8 @@
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/xxhash.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/base/hash.h"
|
||||
#include "xenia/gpu/render_target_cache.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -24,18 +24,183 @@ namespace vulkan {
|
||||
|
||||
class VulkanCommandProcessor;
|
||||
|
||||
// TODO(Triang3l): Create a common base for both the Vulkan and the Direct3D
|
||||
// implementations.
|
||||
class VulkanRenderTargetCache {
|
||||
class VulkanRenderTargetCache final : public RenderTargetCache {
|
||||
public:
|
||||
union RenderPassKey {
|
||||
struct {
|
||||
// If emulating 2x as 4x, set this to 4x for 2x not to create unnecessary
|
||||
// render pass objects.
|
||||
xenos::MsaaSamples msaa_samples : xenos::kMsaaSamplesBits; // 2
|
||||
// << 0 is depth, << 1...4 is color.
|
||||
uint32_t depth_and_color_used : 1 + xenos::kMaxColorRenderTargets; // 7
|
||||
// 0 for unused attachments.
|
||||
// If VK_FORMAT_D24_UNORM_S8_UINT is not supported, this must be kD24FS8
|
||||
// even for kD24S8.
|
||||
xenos::DepthRenderTargetFormat depth_format
|
||||
: xenos::kDepthRenderTargetFormatBits; // 8
|
||||
// Linear or sRGB included if host sRGB is used.
|
||||
xenos::ColorRenderTargetFormat color_0_view_format
|
||||
: xenos::kColorRenderTargetFormatBits; // 12
|
||||
xenos::ColorRenderTargetFormat color_1_view_format
|
||||
: xenos::kColorRenderTargetFormatBits; // 16
|
||||
xenos::ColorRenderTargetFormat color_2_view_format
|
||||
: xenos::kColorRenderTargetFormatBits; // 20
|
||||
xenos::ColorRenderTargetFormat color_3_view_format
|
||||
: xenos::kColorRenderTargetFormatBits; // 24
|
||||
};
|
||||
uint32_t key = 0;
|
||||
struct Hasher {
|
||||
size_t operator()(const RenderPassKey& key) const {
|
||||
return std::hash<uint32_t>{}(key.key);
|
||||
}
|
||||
};
|
||||
bool operator==(const RenderPassKey& other_key) const {
|
||||
return key == other_key.key;
|
||||
}
|
||||
bool operator!=(const RenderPassKey& other_key) const {
|
||||
return !(*this == other_key);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(RenderPassKey) == sizeof(uint32_t));
|
||||
static_assert_size(RenderPassKey, sizeof(uint32_t));
|
||||
|
||||
struct Framebuffer {
|
||||
VkFramebuffer framebuffer;
|
||||
VkExtent2D host_extent;
|
||||
Framebuffer(VkFramebuffer framebuffer, const VkExtent2D& host_extent)
|
||||
: framebuffer(framebuffer), host_extent(host_extent) {}
|
||||
};
|
||||
|
||||
VulkanRenderTargetCache(VulkanCommandProcessor& command_processor,
|
||||
const RegisterFile& register_file);
|
||||
~VulkanRenderTargetCache();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown(bool from_destructor = false);
|
||||
void ClearCache() override;
|
||||
|
||||
// TOOD(Triang3l): Fragment shader interlock.
|
||||
Path GetPath() const override { return Path::kHostRenderTargets; }
|
||||
|
||||
// TODO(Triang3l): Resolution scaling.
|
||||
uint32_t GetResolutionScale() const override { return 1; }
|
||||
|
||||
bool Update(bool is_rasterization_done,
|
||||
uint32_t shader_writes_color_targets) override;
|
||||
// Binding information for the last successful update.
|
||||
RenderPassKey last_update_render_pass_key() const {
|
||||
return last_update_render_pass_key_;
|
||||
}
|
||||
VkRenderPass last_update_render_pass() const {
|
||||
return last_update_render_pass_;
|
||||
}
|
||||
const Framebuffer* last_update_framebuffer() const {
|
||||
return last_update_framebuffer_;
|
||||
}
|
||||
|
||||
// Returns the render pass object, or VK_NULL_HANDLE if failed to create.
|
||||
// A render pass managed by the render target cache may be ended and resumed
|
||||
// at any time (to allow for things like copying and texture loading).
|
||||
VkRenderPass GetRenderPass(RenderPassKey key);
|
||||
|
||||
VkFormat GetDepthVulkanFormat(xenos::DepthRenderTargetFormat format) const;
|
||||
VkFormat GetColorVulkanFormat(xenos::ColorRenderTargetFormat format) const;
|
||||
VkFormat GetColorOwnershipTransferVulkanFormat(
|
||||
xenos::ColorRenderTargetFormat format,
|
||||
bool* is_integer_out = nullptr) const;
|
||||
|
||||
protected:
|
||||
// Can only be destroyed when framebuffers referencing it are destroyed!
|
||||
class VulkanRenderTarget final : public RenderTarget {
|
||||
public:
|
||||
// Takes ownership of the Vulkan objects passed to the constructor.
|
||||
VulkanRenderTarget(RenderTargetKey key,
|
||||
const ui::vulkan::VulkanProvider& provider,
|
||||
VkImage image, VkDeviceMemory memory,
|
||||
VkImageView view_depth_color,
|
||||
VkImageView view_depth_stencil, VkImageView view_stencil,
|
||||
VkImageView view_srgb,
|
||||
VkImageView view_color_transfer_separate)
|
||||
: RenderTarget(key),
|
||||
provider_(provider),
|
||||
image_(image),
|
||||
memory_(memory),
|
||||
view_depth_color_(view_depth_color),
|
||||
view_depth_stencil_(view_depth_stencil),
|
||||
view_stencil_(view_stencil),
|
||||
view_srgb_(view_srgb),
|
||||
view_color_transfer_separate_(view_color_transfer_separate) {}
|
||||
~VulkanRenderTarget();
|
||||
|
||||
VkImage image() const { return image_; }
|
||||
|
||||
VkImageView view_depth_color() const { return view_depth_color_; }
|
||||
VkImageView view_depth_stencil() const { return view_depth_stencil_; }
|
||||
|
||||
VkPipelineStageFlags current_stage_mask() const {
|
||||
return current_stage_mask_;
|
||||
}
|
||||
VkAccessFlags current_access_mask() const { return current_access_mask_; }
|
||||
VkImageLayout current_layout() const { return current_layout_; }
|
||||
void SetUsage(VkPipelineStageFlags stage_mask, VkAccessFlags access_mask,
|
||||
VkImageLayout layout) {
|
||||
current_stage_mask_ = stage_mask;
|
||||
current_access_mask_ = access_mask;
|
||||
current_layout_ = layout;
|
||||
}
|
||||
|
||||
private:
|
||||
const ui::vulkan::VulkanProvider& provider_;
|
||||
|
||||
VkImage image_;
|
||||
VkDeviceMemory memory_;
|
||||
|
||||
// TODO(Triang3l): Per-format drawing views for mutable formats with EDRAM
|
||||
// aliasing without transfers.
|
||||
VkImageView view_depth_color_;
|
||||
// Optional views.
|
||||
VkImageView view_depth_stencil_;
|
||||
VkImageView view_stencil_;
|
||||
VkImageView view_srgb_;
|
||||
VkImageView view_color_transfer_separate_;
|
||||
|
||||
VkPipelineStageFlags current_stage_mask_ = 0;
|
||||
VkAccessFlags current_access_mask_ = 0;
|
||||
VkImageLayout current_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
};
|
||||
|
||||
uint32_t GetMaxRenderTargetWidth() const override;
|
||||
uint32_t GetMaxRenderTargetHeight() const override;
|
||||
|
||||
RenderTarget* CreateRenderTarget(RenderTargetKey key) override;
|
||||
|
||||
// TODO(Triang3l): Check actual unorm24 support.
|
||||
bool IsHostDepthEncodingDifferent(
|
||||
xenos::DepthRenderTargetFormat format) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
VulkanCommandProcessor& command_processor_;
|
||||
|
||||
// RenderPassKey::key -> VkRenderPass.
|
||||
std::unordered_map<uint32_t, VkRenderPass> render_passes_;
|
||||
|
||||
// For host render targets.
|
||||
|
||||
struct FramebufferKey {
|
||||
RenderPassKey render_pass_key;
|
||||
|
||||
// Same as RenderTargetKey::pitch_tiles_at_32bpp.
|
||||
uint32_t pitch_tiles_at_32bpp : 8; // 8
|
||||
// [0, 2047].
|
||||
uint32_t depth_base_tiles : xenos::kEdramBaseTilesBits - 1; // 19
|
||||
uint32_t color_0_base_tiles : xenos::kEdramBaseTilesBits - 1; // 30
|
||||
|
||||
uint32_t color_1_base_tiles : xenos::kEdramBaseTilesBits - 1; // 43
|
||||
uint32_t color_2_base_tiles : xenos::kEdramBaseTilesBits - 1; // 54
|
||||
|
||||
uint32_t color_3_base_tiles : xenos::kEdramBaseTilesBits - 1; // 75
|
||||
|
||||
// Including all the padding, for a stable hash.
|
||||
FramebufferKey() { Reset(); }
|
||||
FramebufferKey(const FramebufferKey& key) {
|
||||
@@ -48,44 +213,27 @@ class VulkanRenderTargetCache {
|
||||
bool operator==(const FramebufferKey& key) const {
|
||||
return std::memcmp(this, &key, sizeof(*this)) == 0;
|
||||
}
|
||||
using Hasher = xe::hash::XXHasher<FramebufferKey>;
|
||||
void Reset() { std::memset(this, 0, sizeof(*this)); }
|
||||
uint64_t GetHash() const { return XXH3_64bits(this, sizeof(*this)); }
|
||||
struct Hasher {
|
||||
size_t operator()(const FramebufferKey& description) const {
|
||||
return size_t(description.GetHash());
|
||||
}
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(FramebufferKey) == sizeof(uint32_t));
|
||||
|
||||
VulkanRenderTargetCache(VulkanCommandProcessor& command_processor,
|
||||
const RegisterFile& register_file);
|
||||
~VulkanRenderTargetCache();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
void ClearCache();
|
||||
|
||||
// Returns the render pass object, or VK_NULL_HANDLE if failed to create.
|
||||
// A render pass managed by the render target cache may be ended and resumed
|
||||
// at any time (to allow for things like copying and texture loading).
|
||||
VkRenderPass GetRenderPass(RenderPassKey key);
|
||||
|
||||
// Returns the framebuffer object, or VK_NULL_HANDLE if failed to create.
|
||||
VkFramebuffer GetFramebuffer(FramebufferKey key);
|
||||
const Framebuffer* GetFramebuffer(
|
||||
RenderPassKey render_pass_key, uint32_t pitch_tiles_at_32bpp,
|
||||
const RenderTarget* const* depth_and_color_render_targets);
|
||||
|
||||
// May dispatch computations.
|
||||
bool UpdateRenderTargets(FramebufferKey& framebuffer_key_out);
|
||||
bool gamma_render_target_as_srgb_ = false;
|
||||
|
||||
private:
|
||||
VulkanCommandProcessor& command_processor_;
|
||||
const RegisterFile& register_file_;
|
||||
|
||||
// RenderPassKey::key -> VkRenderPass.
|
||||
std::unordered_map<uint32_t, VkRenderPass> render_passes_;
|
||||
|
||||
std::unordered_map<FramebufferKey, VkFramebuffer, FramebufferKey::Hasher>
|
||||
std::unordered_map<FramebufferKey, Framebuffer, FramebufferKey::Hasher>
|
||||
framebuffers_;
|
||||
|
||||
RenderPassKey last_update_render_pass_key_;
|
||||
VkRenderPass last_update_render_pass_ = VK_NULL_HANDLE;
|
||||
uint32_t last_update_framebuffer_pitch_tiles_at_32bpp_ = 0;
|
||||
const RenderTarget* const*
|
||||
last_update_framebuffer_attachments_[1 + xenos::kMaxColorRenderTargets] =
|
||||
{};
|
||||
const Framebuffer* last_update_framebuffer_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
|
||||
Reference in New Issue
Block a user