mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
199 lines
6.9 KiB
C++
199 lines
6.9 KiB
C++
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_
|
|
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_
|
|
|
|
#include "common/assert.h"
|
|
#include "graphics/host_gpu/graphicContext.h"
|
|
#include "graphics/host_gpu/renderer/image/imageInfo.h"
|
|
|
|
#include <compare>
|
|
#include <limits>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <span>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace Libs::Graphics {
|
|
|
|
class Buffer;
|
|
class CommandScheduler;
|
|
struct ImageTestAccess;
|
|
|
|
struct ImageId {
|
|
uint32_t index = std::numeric_limits<uint32_t>::max();
|
|
uint32_t generation = 0;
|
|
|
|
[[nodiscard]] explicit operator bool() const noexcept {
|
|
return index != std::numeric_limits<uint32_t>::max();
|
|
}
|
|
auto operator<=>(const ImageId&) const = default;
|
|
};
|
|
|
|
struct CachedImageView {
|
|
ImageViewInfo info;
|
|
vk::ImageView view = nullptr;
|
|
};
|
|
|
|
struct ImageViewCache {
|
|
ImageViewCache() = default;
|
|
KYTY_CLASS_NO_COPY(ImageViewCache);
|
|
|
|
std::mutex mutex;
|
|
std::vector<CachedImageView> views;
|
|
};
|
|
|
|
struct ImageUsage {
|
|
bool texture = false;
|
|
bool storage = false;
|
|
bool render_target = false;
|
|
bool depth_target = false;
|
|
bool video_out = false;
|
|
};
|
|
|
|
struct ImageBinding {
|
|
bool is_bound = false;
|
|
bool is_target = false;
|
|
bool needs_rebind = false;
|
|
bool force_general = false;
|
|
};
|
|
|
|
class Image final {
|
|
public:
|
|
Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageInfo& info);
|
|
~Image();
|
|
KYTY_CLASS_NO_COPY(Image);
|
|
|
|
[[nodiscard]] vk::ImageView FindView(const ImageViewInfo& view_info);
|
|
void AssociateDepth(ImageId image_id) { depth_id = image_id; }
|
|
using Barriers = std::vector<vk::ImageMemoryBarrier2>;
|
|
[[nodiscard]] Barriers GetBarriers(vk::ImageLayout destination_layout,
|
|
vk::AccessFlags2 destination_access,
|
|
vk::PipelineStageFlags2 destination_stage,
|
|
std::optional<ImageSubresourceRange> range);
|
|
void Transit(vk::ImageLayout destination_layout, vk::AccessFlags2 destination_access,
|
|
std::optional<ImageSubresourceRange> range, vk::CommandBuffer command_buffer);
|
|
void Upload(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer, uint64_t offset,
|
|
uint64_t size);
|
|
void Download(std::span<const vk::BufferImageCopy> copies, vk::Buffer buffer, uint64_t offset,
|
|
uint64_t size);
|
|
void CopyImage(Image& source);
|
|
void Resolve(Image& source, const ImageSubresourceRange& source_range,
|
|
const ImageSubresourceRange& destination_range);
|
|
void CopyImageWithBuffer(Image& source, Buffer& buffer);
|
|
void CopyMip(Image& source, uint32_t mip, uint32_t layer);
|
|
|
|
void InvalidateCpuWrite(uint64_t vaddr, uint64_t size) {
|
|
if (ImageRangeOverlaps(info.data.address, info.data.size, vaddr, size)) {
|
|
m_cpu_dirty = true;
|
|
m_maybe_cpu_dirty = false;
|
|
m_maybe_hash_valid = false;
|
|
} else if (ImagePageRangesOverlap(info.data.address, info.data.size, vaddr, size)) {
|
|
m_maybe_cpu_dirty = true;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] bool IsCpuDirty() const { return m_cpu_dirty || m_maybe_cpu_dirty; }
|
|
[[nodiscard]] bool IsDefinitelyCpuDirty() const { return m_cpu_dirty; }
|
|
[[nodiscard]] bool IsMaybeCpuDirty() const { return m_maybe_cpu_dirty; }
|
|
void MarkMaybeCpuDirty() {
|
|
if (!m_cpu_dirty) {
|
|
m_maybe_cpu_dirty = true;
|
|
}
|
|
}
|
|
[[nodiscard]] bool NeedsMaybeCpuHash() const {
|
|
return m_maybe_cpu_dirty && !m_maybe_hash_valid;
|
|
}
|
|
void SetMaybeCpuHash(uint64_t hash) {
|
|
if (!NeedsMaybeCpuHash()) {
|
|
EXIT("image cannot initialize maybe-dirty hash\n");
|
|
}
|
|
m_maybe_cpu_hash = hash;
|
|
m_maybe_hash_valid = true;
|
|
}
|
|
[[nodiscard]] bool ResolveMaybeCpuHash(uint64_t hash) {
|
|
if (!m_maybe_cpu_dirty || !m_maybe_hash_valid || m_cpu_dirty) {
|
|
EXIT("image cannot resolve maybe-dirty hash\n");
|
|
}
|
|
m_maybe_cpu_dirty = false;
|
|
m_maybe_hash_valid = false;
|
|
m_cpu_dirty |= hash != m_maybe_cpu_hash;
|
|
return m_cpu_dirty;
|
|
}
|
|
|
|
void RefreshComplete() {
|
|
if (!IsCpuDirty()) {
|
|
EXIT("clean image cannot complete a refresh\n");
|
|
}
|
|
m_cpu_dirty = false;
|
|
m_maybe_cpu_dirty = false;
|
|
m_maybe_hash_valid = false;
|
|
}
|
|
|
|
[[nodiscard]] bool IsGpuModified() const noexcept { return m_gpu_modified; }
|
|
void MarkGpuModified() noexcept { m_gpu_modified = true; }
|
|
void ClearGpuModified() noexcept { m_gpu_modified = false; }
|
|
|
|
[[nodiscard]] bool IsBufferModified() const noexcept { return m_buffer_modified; }
|
|
void MarkBufferModified() noexcept { m_buffer_modified = true; }
|
|
void ClearBufferModified() noexcept { m_buffer_modified = false; }
|
|
|
|
[[nodiscard]] bool Overlaps(uint64_t address, uint64_t size,
|
|
bool pages = false) const noexcept {
|
|
return pages ? ImagePageRangesOverlap(info.data.address, info.data.size, address, size)
|
|
: ImageRangeOverlaps(info.data.address, info.data.size, address, size);
|
|
}
|
|
[[nodiscard]] bool GpuOverlaps(uint64_t address, uint64_t size) const noexcept {
|
|
return IsGpuModified() && Overlaps(address, size);
|
|
}
|
|
[[nodiscard]] bool SafeToDownload() const noexcept {
|
|
return IsGpuModified() && !IsBufferModified() && !IsCpuDirty();
|
|
}
|
|
[[nodiscard]] bool IsTracked() const noexcept { return track_addr != 0 && track_addr_end != 0; }
|
|
[[nodiscard]] uint64_t AccountedSize() const noexcept {
|
|
return backing.image == nullptr ? 0 : (info.data.size + 1023) & ~uint64_t {1023};
|
|
}
|
|
[[nodiscard]] uint64_t HashGuestEdges() const;
|
|
|
|
ImageInfo info;
|
|
VulkanImage backing;
|
|
ImageViewCache views;
|
|
ImageUsage usage;
|
|
ImageBinding binding;
|
|
bool registered = false;
|
|
mutable uint32_t query_epoch = 0;
|
|
uint64_t track_addr = 0;
|
|
uint64_t track_addr_end = 0;
|
|
ImageId depth_id {};
|
|
uint64_t tick_accessed_last = 0;
|
|
size_t lru_id = 0;
|
|
|
|
private:
|
|
friend struct ImageTestAccess;
|
|
|
|
[[nodiscard]] static vk::ImageAspectFlags FullAspectMask(vk::Format format) noexcept;
|
|
[[nodiscard]] static uint32_t CopyRows(uint64_t row_size, uint32_t rows,
|
|
uint64_t capacity) noexcept;
|
|
[[nodiscard]] static std::pair<uint32_t, uint32_t>
|
|
SanitizeCopyLayers(const Image& source, const Image& destination, uint32_t depth);
|
|
|
|
GraphicContext* m_graphics = nullptr;
|
|
CommandScheduler* m_scheduler = nullptr;
|
|
uint64_t m_maybe_cpu_hash = 0;
|
|
bool m_cpu_dirty = false;
|
|
bool m_maybe_cpu_dirty = false;
|
|
bool m_maybe_hash_valid = false;
|
|
bool m_gpu_modified = false;
|
|
bool m_buffer_modified = false;
|
|
};
|
|
|
|
namespace ImageOps {
|
|
|
|
void Validate(const ImageInfo& info);
|
|
[[nodiscard]] uint32_t RenderTargetTransferFormat(uint32_t bytes_per_element);
|
|
|
|
} // namespace ImageOps
|
|
|
|
} // namespace Libs::Graphics
|
|
|
|
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGE_H_
|