mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
574 lines
22 KiB
C++
574 lines
22 KiB
C++
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGEINFO_H_
|
|
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGEINFO_H_
|
|
|
|
#include "common/assert.h"
|
|
#include "graphics/guest_gpu/gpu_defs.h"
|
|
#include "graphics/guest_gpu/gpu_format.h"
|
|
#include "graphics/host_gpu/regionDefinitions.h"
|
|
#include "graphics/host_gpu/vulkanCommon.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <bit>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
|
|
namespace Libs::Graphics {
|
|
|
|
struct GuestRange {
|
|
uint64_t address = 0;
|
|
uint64_t size = 0;
|
|
|
|
[[nodiscard]] constexpr bool Empty() const noexcept { return address == 0 || size == 0; }
|
|
[[nodiscard]] constexpr bool Valid() const noexcept {
|
|
return !Empty() && address < TRACKER_ADDRESS_SIZE && size <= TRACKER_ADDRESS_SIZE - address;
|
|
}
|
|
[[nodiscard]] constexpr uint64_t End() const noexcept { return address + size; }
|
|
auto operator<=>(const GuestRange&) const = default;
|
|
};
|
|
|
|
enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported };
|
|
|
|
enum class ImageMetadataKind : uint8_t { None, Htile, Dcc };
|
|
|
|
struct ImageMetadataInfo {
|
|
GuestRange range;
|
|
ImageMetadataKind kind = ImageMetadataKind::None;
|
|
uint32_t control = 0;
|
|
VideoOutCompression compression = VideoOutCompression::Uncompressed;
|
|
bool stencil_compressed = false;
|
|
};
|
|
|
|
struct ImageSubresources {
|
|
uint32_t levels = 1;
|
|
uint32_t layers = 1;
|
|
auto operator<=>(const ImageSubresources&) const = default;
|
|
};
|
|
|
|
struct ImageSubresourceRange {
|
|
uint32_t base_level = 0;
|
|
uint32_t level_count = 1;
|
|
uint32_t base_layer = 0;
|
|
uint32_t layer_count = 1;
|
|
auto operator<=>(const ImageSubresourceRange&) const = default;
|
|
};
|
|
|
|
struct ImageMipInfo {
|
|
uint64_t offset = 0;
|
|
uint64_t size = 0;
|
|
uint32_t pitch = 0;
|
|
uint32_t height = 0;
|
|
auto operator<=>(const ImageMipInfo&) const = default;
|
|
};
|
|
|
|
struct ImageInfo {
|
|
GuestRange data;
|
|
GuestRange stencil;
|
|
ImageMetadataInfo metadata;
|
|
uint32_t htile_clear_mask = UINT32_MAX;
|
|
vk::Format pixel_format = vk::Format::eUndefined;
|
|
uint32_t guest_format = 0;
|
|
Prospero::ImageType type = Prospero::ImageType::kColor2D;
|
|
vk::Extent3D extent = {1, 1, 1};
|
|
ImageSubresources resources;
|
|
uint32_t pitch = 0;
|
|
uint32_t bytes_per_block = 0;
|
|
uint32_t samples = 1;
|
|
uint32_t tile_mode = 0;
|
|
bool bgra16 = false;
|
|
std::array<ImageMipInfo, 16> mip_layout {};
|
|
|
|
[[nodiscard]] constexpr bool HasStencil() const noexcept { return !stencil.Empty(); }
|
|
[[nodiscard]] constexpr bool HasMetadata() const noexcept {
|
|
return metadata.kind != ImageMetadataKind::None;
|
|
}
|
|
[[nodiscard]] bool IsDepth() const noexcept;
|
|
[[nodiscard]] bool IsBlock() const noexcept {
|
|
return Prospero::BlockCompressedBytesPerBlock(guest_format) != 0;
|
|
}
|
|
[[nodiscard]] bool IsTiled() const noexcept {
|
|
return tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
|
|
}
|
|
[[nodiscard]] constexpr bool IsVolume() const noexcept {
|
|
return type == Prospero::ImageType::kColor3D;
|
|
}
|
|
[[nodiscard]] constexpr bool IsLayered() const noexcept {
|
|
return !IsVolume() && resources.layers > 1;
|
|
}
|
|
[[nodiscard]] constexpr uint32_t TransferLayers() const noexcept {
|
|
return IsVolume() ? extent.depth : resources.layers;
|
|
}
|
|
[[nodiscard]] vk::Extent2D BlockExtent() const noexcept {
|
|
const auto shift = Prospero::BlockCompressedBytesPerBlock(guest_format) != 0 ? 2u : 0u;
|
|
return {pitch >> shift, extent.height >> shift};
|
|
}
|
|
[[nodiscard]] bool IsCompatible(const ImageInfo& other) const noexcept {
|
|
return pixel_format == other.pixel_format && samples == other.samples &&
|
|
bytes_per_block == other.bytes_per_block;
|
|
}
|
|
[[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept {
|
|
if (!IsCompatible(container) || tile_mode != container.tile_mode || resources.levels != 1 ||
|
|
container.resources.layers == 0 ||
|
|
container.resources.levels > container.mip_layout.size()) {
|
|
return -1;
|
|
}
|
|
if (HasStencil() != container.HasStencil() ||
|
|
(HasStencil() && (stencil.address < container.stencil.address ||
|
|
stencil.End() > container.stencil.End()))) {
|
|
return -1;
|
|
}
|
|
|
|
int32_t mip = -1;
|
|
for (uint32_t level = 0; level < container.resources.levels; level++) {
|
|
const auto& layout = container.mip_layout[level];
|
|
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
|
container.data.address > UINT64_MAX - layout.offset) {
|
|
continue;
|
|
}
|
|
const auto mip_base = container.data.address + layout.offset;
|
|
const auto slice_size = layout.size / container.resources.layers;
|
|
if (slice_size == 0 || mip_base > UINT64_MAX - layout.size) {
|
|
continue;
|
|
}
|
|
const auto mip_end = mip_base + layout.size;
|
|
if (data.address >= mip_base && data.address < mip_end &&
|
|
(data.address - mip_base) % slice_size == 0) {
|
|
mip = static_cast<int32_t>(level);
|
|
break;
|
|
}
|
|
}
|
|
if (mip < 0) {
|
|
return -1;
|
|
}
|
|
|
|
const auto level = static_cast<uint32_t>(mip);
|
|
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
|
extent.height != std::max(container.extent.height >> level, 1u)) {
|
|
return -1;
|
|
}
|
|
const auto mip_depth = std::max(container.extent.depth >> level, 1u);
|
|
if (container.type == Prospero::ImageType::kColor3D &&
|
|
type == Prospero::ImageType::kColor2D) {
|
|
if (resources.layers != mip_depth) {
|
|
return -1;
|
|
}
|
|
} else if (type != container.type) {
|
|
return -1;
|
|
}
|
|
return mip;
|
|
}
|
|
[[nodiscard]] int32_t SliceOf(const ImageInfo& container, int32_t mip) const noexcept {
|
|
if (!IsCompatible(container) || type != container.type || mip < 0 ||
|
|
static_cast<uint32_t>(mip) >= container.resources.levels ||
|
|
container.resources.levels > container.mip_layout.size() ||
|
|
container.resources.layers == 0 || data.size == 0) {
|
|
return -1;
|
|
}
|
|
const auto level = static_cast<uint32_t>(mip);
|
|
if (extent.width != std::max(container.extent.width >> level, 1u) ||
|
|
extent.height != std::max(container.extent.height >> level, 1u)) {
|
|
return -1;
|
|
}
|
|
const auto& layout = container.mip_layout[level];
|
|
if (layout.size == 0 || layout.size % container.resources.layers != 0 ||
|
|
container.data.address > UINT64_MAX - layout.offset) {
|
|
return -1;
|
|
}
|
|
const auto slice_size = layout.size / container.resources.layers;
|
|
if (slice_size == 0 || data.size % slice_size != 0) {
|
|
return -1;
|
|
}
|
|
const auto mip_base = container.data.address + layout.offset;
|
|
if (data.address < mip_base) {
|
|
return -1;
|
|
}
|
|
const auto address_delta = data.address - mip_base;
|
|
if (address_delta % data.size != 0 || address_delta / data.size > INT32_MAX) {
|
|
return -1;
|
|
}
|
|
return static_cast<int32_t>(address_delta / data.size);
|
|
}
|
|
};
|
|
|
|
struct ImageViewInfo {
|
|
vk::Format format = vk::Format::eUndefined;
|
|
vk::ImageViewType type = vk::ImageViewType::e2D;
|
|
vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor;
|
|
uint32_t base_level = 0;
|
|
uint32_t level_count = 1;
|
|
uint32_t base_layer = 0;
|
|
uint32_t layer_count = 1;
|
|
vk::ComponentMapping mapping = {};
|
|
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eSampled;
|
|
|
|
[[nodiscard]] bool operator==(const ImageViewInfo& rhs) const noexcept {
|
|
return format == rhs.format && type == rhs.type && aspect == rhs.aspect &&
|
|
base_level == rhs.base_level && level_count == rhs.level_count &&
|
|
base_layer == rhs.base_layer && layer_count == rhs.layer_count &&
|
|
mapping.r == rhs.mapping.r && mapping.g == rhs.mapping.g &&
|
|
mapping.b == rhs.mapping.b && mapping.a == rhs.mapping.a && usage == rhs.usage;
|
|
}
|
|
};
|
|
|
|
struct DepthFormatPolicy {
|
|
Prospero::DepthFormat depth_format;
|
|
Prospero::BufferFormat guest_format;
|
|
uint32_t bytes_per_element;
|
|
vk::Format sampled_view_format;
|
|
vk::Format depth_attachment_format;
|
|
std::array<vk::Format, 3> stencil_attachment_formats;
|
|
};
|
|
|
|
inline constexpr std::array<DepthFormatPolicy, 2> DEPTH_FORMAT_POLICIES {{
|
|
{Prospero::DepthFormat::kZ16,
|
|
Prospero::BufferFormat::k16UNorm,
|
|
2,
|
|
vk::Format::eR16Unorm,
|
|
vk::Format::eD16Unorm,
|
|
{vk::Format::eD16UnormS8Uint, vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint}},
|
|
{Prospero::DepthFormat::kZ32F,
|
|
Prospero::BufferFormat::k32Float,
|
|
4,
|
|
vk::Format::eR32Sfloat,
|
|
vk::Format::eD32Sfloat,
|
|
{vk::Format::eD32SfloatS8Uint, vk::Format::eUndefined, vk::Format::eUndefined}},
|
|
}};
|
|
|
|
[[nodiscard]] inline constexpr const DepthFormatPolicy*
|
|
FindDepthFormatPolicy(uint32_t depth_format) noexcept {
|
|
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
|
if (Prospero::GpuEnumValue(policy.depth_format) == depth_format) {
|
|
return &policy;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr const DepthFormatPolicy*
|
|
FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept {
|
|
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
|
if (Prospero::GpuEnumValue(policy.guest_format) == guest_format) {
|
|
return &policy;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool IsStencilAttachmentFormat(const DepthFormatPolicy& policy,
|
|
vk::Format format) noexcept {
|
|
for (const auto candidate: policy.stencil_attachment_formats) {
|
|
if (candidate != vk::Format::eUndefined && candidate == format) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr vk::Format DepthAttachmentFormat(const DepthFormatPolicy& policy,
|
|
bool has_stencil) noexcept {
|
|
return has_stencil ? policy.stencil_attachment_formats.front() : policy.depth_attachment_format;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr vk::Format DepthAttachmentFormat(uint32_t depth_format,
|
|
uint32_t stencil_format) noexcept {
|
|
bool has_stencil = false;
|
|
switch (static_cast<Prospero::StencilFormat>(stencil_format)) {
|
|
case Prospero::StencilFormat::kInvalid: break;
|
|
case Prospero::StencilFormat::k8UInt: has_stencil = true; break;
|
|
default: return vk::Format::eUndefined;
|
|
}
|
|
const auto* policy = FindDepthFormatPolicy(depth_format);
|
|
return policy == nullptr ? vk::Format::eUndefined : DepthAttachmentFormat(*policy, has_stencil);
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr vk::ImageUsageFlags DepthTargetImageUsage() noexcept {
|
|
return vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eSampled |
|
|
vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr vk::Format DepthAspectTransferFormat(vk::Format format) noexcept {
|
|
switch (format) {
|
|
case vk::Format::eD16Unorm:
|
|
case vk::Format::eD16UnormS8Uint: return vk::Format::eD16Unorm;
|
|
case vk::Format::eD24UnormS8Uint: return vk::Format::eX8D24UnormPack32;
|
|
case vk::Format::eD32Sfloat:
|
|
case vk::Format::eD32SfloatS8Uint: return vk::Format::eD32Sfloat;
|
|
default: return vk::Format::eUndefined;
|
|
}
|
|
}
|
|
|
|
inline bool ImageInfo::IsDepth() const noexcept {
|
|
return DepthAspectTransferFormat(pixel_format) != vk::Format::eUndefined;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr uint32_t DepthAspectTransferBytes(vk::Format format) noexcept {
|
|
switch (DepthAspectTransferFormat(format)) {
|
|
case vk::Format::eD16Unorm: return 2;
|
|
case vk::Format::eX8D24UnormPack32:
|
|
case vk::Format::eD32Sfloat: return 4;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr uint32_t EncodeD16AsD24(uint16_t value) noexcept {
|
|
return static_cast<uint32_t>((static_cast<uint64_t>(value) * 0x00ffffffu + 0x7fffu) / 0xffffu);
|
|
}
|
|
|
|
[[nodiscard]] inline uint32_t EncodeD16AsD32(uint16_t value) noexcept {
|
|
return std::bit_cast<uint32_t>(static_cast<float>(value) / 65535.0f);
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
|
|
uint32_t guest_format,
|
|
vk::Format view_format) noexcept {
|
|
const auto* policy = FindGuestDepthFormatPolicy(guest_format);
|
|
return policy != nullptr && view_format == policy->sampled_view_format &&
|
|
(image_format == policy->depth_attachment_format ||
|
|
IsStencilAttachmentFormat(*policy, image_format));
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(vk::Format image_format,
|
|
vk::Format view_format) noexcept {
|
|
for (const auto& policy: DEPTH_FORMAT_POLICIES) {
|
|
if (IsSupportedSampledDepthFormat(image_format, Prospero::GpuEnumValue(policy.guest_format),
|
|
view_format)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool IsSupportedDepthTargetFormat(const ImageInfo& info) {
|
|
const auto* policy = FindGuestDepthFormatPolicy(info.guest_format);
|
|
return policy != nullptr && info.bytes_per_block == policy->bytes_per_element &&
|
|
(info.HasStencil() ? IsStencilAttachmentFormat(*policy, info.pixel_format)
|
|
: info.pixel_format == policy->depth_attachment_format);
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool IsSupportedDepthPlaneReadback(const ImageInfo& info) {
|
|
if (!IsSupportedDepthTargetFormat(info)) {
|
|
return false;
|
|
}
|
|
const auto transfer_bytes = DepthAspectTransferBytes(info.pixel_format);
|
|
return transfer_bytes == info.bytes_per_block ||
|
|
(info.bytes_per_block == sizeof(uint16_t) && transfer_bytes == sizeof(uint32_t));
|
|
}
|
|
|
|
[[nodiscard]] inline VideoOutCompression
|
|
ClassifyVideoOutCompression(bool compressed, uint64_t metadata_address, uint32_t dcc_control,
|
|
uint64_t dcc_clear_color) noexcept {
|
|
constexpr uint32_t DCC_256_256_0 = 0x00000048u;
|
|
constexpr uint32_t DCC_256_64_64 = 0x00000208u;
|
|
if (!compressed) {
|
|
return metadata_address == 0 && dcc_control == 0 && dcc_clear_color == 0
|
|
? VideoOutCompression::Uncompressed
|
|
: VideoOutCompression::Unsupported;
|
|
}
|
|
if (metadata_address == 0 || (metadata_address & 0xffu) != 0 || dcc_clear_color != 0) {
|
|
return VideoOutCompression::Unsupported;
|
|
}
|
|
switch (dcc_control) {
|
|
case DCC_256_256_0: return VideoOutCompression::Dcc256_256_0;
|
|
case DCC_256_64_64: return VideoOutCompression::Dcc256_64_64;
|
|
default: return VideoOutCompression::Unsupported;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool
|
|
CanUseVideoOutNativeWithoutUpload(VideoOutCompression compression, bool render_target,
|
|
bool gpu_modified, bool guest_modified) noexcept {
|
|
return compression != VideoOutCompression::Uncompressed &&
|
|
compression != VideoOutCompression::Unsupported && !guest_modified &&
|
|
(render_target || gpu_modified);
|
|
}
|
|
|
|
struct VideoOutPixelFormatInfo {
|
|
vk::Format format = vk::Format::eUndefined;
|
|
uint32_t guest_format = 0;
|
|
uint32_t bytes_per_element = 0;
|
|
bool bgra16 = false;
|
|
};
|
|
|
|
struct VideoOutFormatPolicy {
|
|
uint64_t pixel_format;
|
|
VideoOutPixelFormatInfo info;
|
|
};
|
|
|
|
inline constexpr std::array<VideoOutFormatPolicy, 6> VIDEO_OUT_FORMAT_POLICIES {{
|
|
{0x8000000022000000ull,
|
|
{vk::Format::eR8G8B8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
|
|
false}},
|
|
{0x8000000000000000ull,
|
|
{vk::Format::eB8G8R8A8Srgb, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4,
|
|
false}},
|
|
{0x8100000022000000ull,
|
|
{vk::Format::eA2B10G10R10UnormPack32,
|
|
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
|
|
{0x8100000000000000ull,
|
|
{vk::Format::eA2R10G10B10UnormPack32,
|
|
Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}},
|
|
{0xc001000622000000ull,
|
|
{vk::Format::eR16G16B16A16Sfloat,
|
|
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, false}},
|
|
{0xc001000600000000ull,
|
|
{vk::Format::eR16G16B16A16Sfloat,
|
|
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, true}},
|
|
}};
|
|
|
|
[[nodiscard]] inline bool DecodeVideoOutPixelFormat(uint64_t pixel_format,
|
|
VideoOutPixelFormatInfo& info) {
|
|
for (const auto& policy: VIDEO_OUT_FORMAT_POLICIES) {
|
|
if (policy.pixel_format == pixel_format) {
|
|
info = policy.info;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] inline bool IsSupportedVideoOutFormat(const ImageInfo& info) {
|
|
for (const auto& policy: VIDEO_OUT_FORMAT_POLICIES) {
|
|
if (info.pixel_format == policy.info.format &&
|
|
info.guest_format == policy.info.guest_format &&
|
|
info.bytes_per_block == policy.info.bytes_per_element &&
|
|
info.bgra16 == policy.info.bgra16) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool
|
|
IsSupportedDisplayRenderTargetTileMode(uint32_t tile_mode) noexcept {
|
|
return tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool IsSupportedStandard64RenderTarget(const ImageInfo& info) {
|
|
if (info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kStandard64KB) ||
|
|
info.data.address == 0 || (info.data.address & 0xffffu) != 0 || info.extent.width == 0 ||
|
|
info.extent.height == 0 || info.bytes_per_block != 4 || info.resources.levels != 1 ||
|
|
info.resources.layers != 1 || info.samples != 1) {
|
|
return false;
|
|
}
|
|
const auto expected_pitch =
|
|
(static_cast<uint64_t>(info.extent.width) + 127u) & ~uint64_t {127u};
|
|
const auto padded_height =
|
|
(static_cast<uint64_t>(info.extent.height) + 127u) & ~uint64_t {127u};
|
|
return expected_pitch <= UINT32_MAX && info.pitch == expected_pitch &&
|
|
expected_pitch <= UINT64_MAX / padded_height / info.bytes_per_block &&
|
|
info.data.size == expected_pitch * padded_height * info.bytes_per_block;
|
|
}
|
|
|
|
[[nodiscard]] inline constexpr bool IsTiledRenderTarget(const ImageInfo& info) noexcept {
|
|
return info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
|
|
IsSupportedStandard64RenderTarget(info);
|
|
}
|
|
|
|
[[nodiscard]] inline bool DecodePackedColorClear(vk::Format format, uint32_t packed,
|
|
vk::ClearColorValue& clear) {
|
|
vk::ClearColorValue next {};
|
|
const auto unorm8 = [](uint32_t value) { return static_cast<float>(value & 0xffu) / 255.0f; };
|
|
const auto srgb8 = [](uint32_t value) {
|
|
const auto encoded = static_cast<float>(value & 0xffu) / 255.0f;
|
|
return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
|
};
|
|
switch (format) {
|
|
case vk::Format::eR32Uint: next.uint32[0] = packed; break;
|
|
case vk::Format::eR32Sint: next.int32[0] = static_cast<int32_t>(packed); break;
|
|
case vk::Format::eR8G8B8A8Srgb:
|
|
next.float32[0] = srgb8(packed);
|
|
next.float32[1] = srgb8(packed >> 8u);
|
|
next.float32[2] = srgb8(packed >> 16u);
|
|
next.float32[3] = unorm8(packed >> 24u);
|
|
break;
|
|
case vk::Format::eB8G8R8A8Srgb:
|
|
next.float32[0] = srgb8(packed >> 16u);
|
|
next.float32[1] = srgb8(packed >> 8u);
|
|
next.float32[2] = srgb8(packed);
|
|
next.float32[3] = unorm8(packed >> 24u);
|
|
break;
|
|
case vk::Format::eR8G8B8A8Unorm:
|
|
next.float32[0] = unorm8(packed);
|
|
next.float32[1] = unorm8(packed >> 8u);
|
|
next.float32[2] = unorm8(packed >> 16u);
|
|
next.float32[3] = unorm8(packed >> 24u);
|
|
break;
|
|
case vk::Format::eB8G8R8A8Unorm:
|
|
next.float32[0] = unorm8(packed >> 16u);
|
|
next.float32[1] = unorm8(packed >> 8u);
|
|
next.float32[2] = unorm8(packed);
|
|
next.float32[3] = unorm8(packed >> 24u);
|
|
break;
|
|
case vk::Format::eA2B10G10R10UnormPack32:
|
|
next.float32[0] = static_cast<float>(packed & 0x3ffu) / 1023.0f;
|
|
next.float32[1] = static_cast<float>((packed >> 10u) & 0x3ffu) / 1023.0f;
|
|
next.float32[2] = static_cast<float>((packed >> 20u) & 0x3ffu) / 1023.0f;
|
|
next.float32[3] = static_cast<float>((packed >> 30u) & 0x3u) / 3.0f;
|
|
break;
|
|
case vk::Format::eA2R10G10B10UnormPack32:
|
|
next.float32[0] = static_cast<float>((packed >> 20u) & 0x3ffu) / 1023.0f;
|
|
next.float32[1] = static_cast<float>((packed >> 10u) & 0x3ffu) / 1023.0f;
|
|
next.float32[2] = static_cast<float>(packed & 0x3ffu) / 1023.0f;
|
|
next.float32[3] = static_cast<float>((packed >> 30u) & 0x3u) / 3.0f;
|
|
break;
|
|
default: return false;
|
|
}
|
|
clear = next;
|
|
return true;
|
|
}
|
|
|
|
[[nodiscard]] inline bool DecodePackedStencilClear(uint32_t packed, uint8_t& clear) {
|
|
const auto value = static_cast<uint8_t>(packed);
|
|
if (packed != static_cast<uint32_t>(value) * 0x01010101u) {
|
|
return false;
|
|
}
|
|
clear = value;
|
|
return true;
|
|
}
|
|
|
|
[[nodiscard]] inline bool DecodePackedDepthClear(vk::Format format, uint32_t packed, float& clear) {
|
|
if (format != vk::Format::eD32Sfloat && format != vk::Format::eD32SfloatS8Uint) {
|
|
return false;
|
|
}
|
|
const auto value = std::bit_cast<float>(packed);
|
|
if (!std::isfinite(value) || value < 0.0f || value > 1.0f) {
|
|
return false;
|
|
}
|
|
clear = value;
|
|
return true;
|
|
}
|
|
|
|
[[nodiscard]] inline bool ImageRangeOverlaps(uint64_t left, uint64_t left_size, uint64_t right,
|
|
uint64_t right_size) {
|
|
if (left_size == 0 || right_size == 0 || left > UINT64_MAX - left_size ||
|
|
right > UINT64_MAX - right_size) {
|
|
EXIT("invalid image overlap range\n");
|
|
}
|
|
return left < right + right_size && right < left + left_size;
|
|
}
|
|
|
|
[[nodiscard]] inline bool ImageRangeOverlaps(GuestRange left, GuestRange right) {
|
|
return ImageRangeOverlaps(left.address, left.size, right.address, right.size);
|
|
}
|
|
|
|
[[nodiscard]] inline bool ImagePageRangesOverlap(uint64_t left, uint64_t left_size, uint64_t right,
|
|
uint64_t right_size) {
|
|
if (left_size == 0 || right_size == 0 || left > UINT64_MAX - left_size ||
|
|
right > UINT64_MAX - right_size) {
|
|
EXIT("invalid image page-overlap range\n");
|
|
}
|
|
const auto left_first = left / TRACKER_PAGE_SIZE;
|
|
const auto left_last = (left + left_size - 1) / TRACKER_PAGE_SIZE;
|
|
const auto right_first = right / TRACKER_PAGE_SIZE;
|
|
const auto right_last = (right + right_size - 1) / TRACKER_PAGE_SIZE;
|
|
return left_first <= right_last && right_first <= left_last;
|
|
}
|
|
|
|
[[nodiscard]] inline bool ImagePageRangesOverlap(GuestRange left, GuestRange right) {
|
|
return ImagePageRangesOverlap(left.address, left.size, right.address, right.size);
|
|
}
|
|
|
|
} // namespace Libs::Graphics
|
|
|
|
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGEINFO_H_
|