gpu: fix 10-11-11 render-target storage

Co-authored-by: Çağatay Güçlü <33038942+defektu@users.noreply.github.com>
This commit is contained in:
nmzik
2026-10-04 21:18:19 +04:00
parent 07c84876a1
commit 5940e623d5
15 changed files with 319 additions and 88 deletions
+1 -1
View File
@@ -259,7 +259,7 @@ set(gpu_tiler_shader_names
depth depth
promote_d16 promote_d16
demote_d16 demote_d16
swap_bgra16 color_transform
) )
file(GLOB gpu_tiler_shader_includes CONFIGURE_DEPENDS "${gpu_tiler_shader_dir}/gpu_tiler_*.inc") file(GLOB gpu_tiler_shader_includes CONFIGURE_DEPENDS "${gpu_tiler_shader_dir}/gpu_tiler_*.inc")
foreach(shader_name IN LISTS gpu_tiler_shader_names) foreach(shader_name IN LISTS gpu_tiler_shader_names)
+3 -2
View File
@@ -21,7 +21,7 @@ struct FormatInfo {
constexpr FormatInfo kFormatInfo[] = { constexpr FormatInfo kFormatInfo[] = {
{BufferFormat::k8UNorm, 1, 0, 1, true, false}, {BufferFormat::k8UNorm, 1, 0, 1, true, false},
{BufferFormat::k8UScaled, 1, 0, 0, true, false}, {BufferFormat::k8UScaled, 1, 0, 0, true, false},
{BufferFormat::k8SNorm, 0, 0, 1, false, false}, {BufferFormat::k8SNorm, 1, 0, 1, false, false},
{BufferFormat::k8UInt, 1, 0, 1, true, true}, {BufferFormat::k8UInt, 1, 0, 1, true, true},
{BufferFormat::k16UNorm, 2, 0, 2, true, false}, {BufferFormat::k16UNorm, 2, 0, 2, true, false},
{BufferFormat::k16SNorm, 2, 0, 2, true, false}, {BufferFormat::k16SNorm, 2, 0, 2, true, false},
@@ -43,6 +43,7 @@ constexpr FormatInfo kFormatInfo[] = {
{BufferFormat::k16_16Float, 4, 0, 4, true, false}, {BufferFormat::k16_16Float, 4, 0, 4, true, false},
{BufferFormat::k11_11_10UInt, 4, 0, 4, true, true}, {BufferFormat::k11_11_10UInt, 4, 0, 4, true, true},
{BufferFormat::k11_11_10Float, 4, 0, 4, true, false}, {BufferFormat::k11_11_10Float, 4, 0, 4, true, false},
{BufferFormat::k10_11_11Float, 4, 0, 4, true, false},
{BufferFormat::k10_10_10_2UNorm, 4, 0, 4, true, false}, {BufferFormat::k10_10_10_2UNorm, 4, 0, 4, true, false},
{BufferFormat::k10_10_10_2UInt, 4, 0, 4, true, true}, {BufferFormat::k10_10_10_2UInt, 4, 0, 4, true, true},
{BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false}, {BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false},
@@ -69,7 +70,7 @@ constexpr FormatInfo kFormatInfo[] = {
{BufferFormat::k9_9_9_5Float, 4, 0, 0, true, false}, {BufferFormat::k9_9_9_5Float, 4, 0, 0, true, false},
{BufferFormat::k5_6_5UNorm, 2, 0, 2, true, false}, {BufferFormat::k5_6_5UNorm, 2, 0, 2, true, false},
{BufferFormat::k5_5_5_1UNorm, 2, 0, 2, true, false}, {BufferFormat::k5_5_5_1UNorm, 2, 0, 2, true, false},
{BufferFormat::k1_5_5_5UNorm, 0, 0, 2, false, false}, {BufferFormat::k1_5_5_5UNorm, 2, 0, 2, false, false},
{BufferFormat::k4_4_4_4UNorm, 2, 0, 2, true, false}, {BufferFormat::k4_4_4_4UNorm, 2, 0, 2, true, false},
{BufferFormat::kFmask8_S2_F1, 1, 0, 1, true, false}, {BufferFormat::kFmask8_S2_F1, 1, 0, 1, true, false},
{BufferFormat::kFmask8_S4_F1, 1, 0, 1, true, false}, {BufferFormat::kFmask8_S4_F1, 1, 0, 1, true, false},
+19 -13
View File
@@ -195,6 +195,9 @@ bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& request
if (cached.tile_mode != requested.tile_mode) { if (cached.tile_mode != requested.tile_mode) {
return false; return false;
} }
if (cached.GetColorTransform() != requested.GetColorTransform()) {
return false;
}
if (!ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format) || if (!ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format) ||
cached.type != requested.type) { cached.type != requested.type) {
return false; return false;
@@ -634,6 +637,7 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) {
const bool source_depth = source.info.IsDepth(); const bool source_depth = source.info.IsDepth();
const bool dest_depth = destination.info.IsDepth(); const bool dest_depth = destination.info.IsDepth();
const bool direct_copy = const bool direct_copy =
source.info.GetColorTransform() == destination.info.GetColorTransform() &&
(source.backing.image_type == destination.backing.image_type || (source.backing.image_type == destination.backing.image_type ||
(source.backing.image_type != vk::ImageType::e1D && (source.backing.image_type != vk::ImageType::e1D &&
destination.backing.image_type != vk::ImageType::e1D)) && destination.backing.image_type != vk::ImageType::e1D)) &&
@@ -647,7 +651,7 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) {
EXIT("TextureCache: cross-format multisample image copy is unsupported\n"); EXIT("TextureCache: cross-format multisample image copy is unsupported\n");
} }
auto& copy_buffer = m_buffer_cache.GetUtilityBuffer(MemoryUsage::DeviceLocal); auto& copy_buffer = m_buffer_cache.GetUtilityBuffer(MemoryUsage::DeviceLocal);
destination.CopyImageWithBuffer(source, copy_buffer); destination.CopyImageWithBuffer(source, copy_buffer, m_tiler);
} }
if (source.IsGpuModified()) { if (source.IsGpuModified()) {
destination.MarkGpuModified(); destination.MarkGpuModified();
@@ -801,6 +805,9 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
} }
return {merged_id}; return {merged_id};
} }
if (requested.GetColorTransform() != cached.info.GetColorTransform()) {
return {ExpandImage(requested, cached_id)};
}
if (requested.IsBlock() && !cached.info.IsBlock()) { if (requested.IsBlock() && !cached.info.IsBlock()) {
return {ExpandImage(requested, cached_id)}; return {ExpandImage(requested, cached_id)};
} }
@@ -905,8 +912,8 @@ struct TextureCache::TextureTransfer {
TextureUploadLayout layout; TextureUploadLayout layout;
std::vector<vk::BufferImageCopy> regions; std::vector<vk::BufferImageCopy> regions;
std::vector<GpuTileInfo> tiles; std::vector<GpuTileInfo> tiles;
bool swap_bgra16 = false; ColorTransform color_transform = ColorTransform::None;
bool valid = false; bool valid = false;
[[nodiscard]] uint64_t LinearSize() const { [[nodiscard]] uint64_t LinearSize() const {
uint64_t size = 0; uint64_t size = 0;
@@ -935,7 +942,7 @@ TextureCache::BuildTextureTransfer(const Image& image, BindingType binding,
const char* owner = "TextureCache readback"; const char* owner = "TextureCache readback";
TextureTransfer transfer; TextureTransfer transfer;
transfer.swap_bgra16 = info.bgra16; transfer.color_transform = info.GetColorTransform();
if (upload) { if (upload) {
if ((render_target || video_out) && if ((render_target || video_out) &&
(info.resources.layers == 0 || info.data.size % info.resources.layers != 0 || (info.resources.layers == 0 || info.data.size % info.resources.layers != 0 ||
@@ -1023,10 +1030,9 @@ void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_off
TileManager::Result linear {source.Handle(), source_offset, info.data.size}; TileManager::Result linear {source.Handle(), source_offset, info.data.size};
if (!transfer.tiles.empty()) { if (!transfer.tiles.empty()) {
linear = m_tiler.Detile(source.Handle(), source_offset, info.data.size, linear = m_tiler.Detile(source.Handle(), source_offset, info.data.size,
transfer.LinearSize(), transfer.tiles); transfer.LinearSize(), transfer.tiles, transfer.color_transform);
} } else if (transfer.color_transform != ColorTransform::None) {
if (transfer.swap_bgra16) { linear = m_tiler.TransformColor(linear, transfer.color_transform, true);
linear = m_tiler.SwapBgra16(linear);
} }
upload(transfer.regions, linear); upload(transfer.regions, linear);
return; return;
@@ -1741,14 +1747,14 @@ void TextureCache::DownloadImage(Image& image, Buffer& destination, uint64_t des
} }
auto& texture = transfer.texture; auto& texture = transfer.texture;
const auto transform = texture.swap_bgra16 ? TileManager::ColorTransform::SwapBgra16 const auto transform = texture.color_transform;
: TileManager::ColorTransform::None;
if (texture.tiles.empty()) { if (texture.tiles.empty()) {
if (transform == TileManager::ColorTransform::SwapBgra16) { if (transform != ColorTransform::None) {
auto linear = m_tiler.GetScratchBuffer(destination_size); auto linear = m_tiler.GetScratchBuffer(destination_size);
image.Download(texture.regions, linear.buffer, 0, linear.size); image.Download(texture.regions, linear.buffer, 0, linear.size);
m_tiler.SwapBgra16(linear, m_tiler.TransformColor(linear,
{destination.Handle(), destination_offset, destination_size}); {destination.Handle(), destination_offset, destination_size},
transform, false);
return; return;
} }
for (auto& copy: texture.regions) { for (auto& copy: texture.regions) {
@@ -264,7 +264,7 @@ void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColor
desc.type = TextureCache::BindingType::RenderTarget; desc.type = TextureCache::BindingType::RenderTarget;
desc.info.data = {rt.base.addr, backing_size}; desc.info.data = {rt.base.addr, backing_size};
desc.info.pixel_format = target_format.format; desc.info.pixel_format = target_format.format;
desc.info.guest_format = transfer_format; desc.info.guest_format = target_format.guest_format;
desc.info.type = image_type; desc.info.type = image_type;
desc.info.extent = {width, height, depth}; desc.info.extent = {width, height, depth};
desc.info.resources = {levels, volume ? 1u : view.image_layers}; desc.info.resources = {levels, volume ? 1u : view.image_layers};
+17 -4
View File
@@ -6,6 +6,7 @@
#include "graphics/host_gpu/renderer/cache/streamBuffer.h" #include "graphics/host_gpu/renderer/cache/streamBuffer.h"
#include "graphics/host_gpu/renderer/commandScheduler.h" #include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/image/imageView.h" #include "graphics/host_gpu/renderer/image/imageView.h"
#include "graphics/host_gpu/renderer/image/tiler.h"
#include "graphics/host_gpu/renderer/renderTarget.h" #include "graphics/host_gpu/renderer/renderTarget.h"
#include "kernel/memory.h" #include "kernel/memory.h"
@@ -456,7 +457,7 @@ uint32_t Image::CopyRows(uint64_t row_size, uint32_t rows, uint64_t capacity) no
return static_cast<uint32_t>(std::min<uint64_t>(rows, capacity / row_size)); return static_cast<uint32_t>(std::min<uint64_t>(rows, capacity / row_size));
} }
void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) { void Image::CopyImageWithBuffer(Image& source, Buffer& buffer, TileManager& tiler) {
EXIT_IF(buffer.Handle() == nullptr || source.backing.samples != 1 || backing.samples != 1); EXIT_IF(buffer.Handle() == nullptr || source.backing.samples != 1 || backing.samples != 1);
m_scheduler.EndRendering(); m_scheduler.EndRendering();
const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels); const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels);
@@ -474,6 +475,8 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
const uint32_t destination_block = info.IsBlock() ? 4u : 1u; const uint32_t destination_block = info.IsBlock() ? 4u : 1u;
EXIT_IF(levels == 0 || source_bytes == 0 || source_bytes != destination_bytes || EXIT_IF(levels == 0 || source_bytes == 0 || source_bytes != destination_bytes ||
source_block != destination_block); source_block != destination_block);
const auto source_transform = source.info.GetColorTransform();
const auto target_transform = info.GetColorTransform();
vk::BufferMemoryBarrier2 barrier {}; vk::BufferMemoryBarrier2 barrier {};
barrier.srcStageMask = vk::PipelineStageFlagBits2::eTransfer; barrier.srcStageMask = vk::PipelineStageFlagBits2::eTransfer;
@@ -535,9 +538,19 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
command.copyImageToBuffer(source.backing.image, command.copyImageToBuffer(source.backing.image,
vk::ImageLayout::eTransferSrcOptimal, buffer.Handle(), vk::ImageLayout::eTransferSrcOptimal, buffer.Handle(),
source_copy); source_copy);
barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite; if (source_transform != target_transform) {
barrier.dstAccessMask = vk::AccessFlagBits2::eTransferRead; const TileManager::Result bytes {buffer.Handle(), 0, copy_size};
command.pipelineBarrier2(dependency); if (source_transform != ColorTransform::None) {
tiler.TransformColor(bytes, bytes, source_transform, false);
}
if (target_transform != ColorTransform::None) {
tiler.TransformColor(bytes, bytes, target_transform, true);
}
} else {
barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits2::eTransferRead;
command.pipelineBarrier2(dependency);
}
command.copyBufferToImage(buffer.Handle(), backing.image, command.copyBufferToImage(buffer.Handle(), backing.image,
vk::ImageLayout::eTransferDstOptimal, destination_copy); vk::ImageLayout::eTransferDstOptimal, destination_copy);
} }
+2 -1
View File
@@ -18,6 +18,7 @@ namespace Libs::Graphics {
class Buffer; class Buffer;
class CommandScheduler; class CommandScheduler;
class TileManager;
struct ImageTestAccess; struct ImageTestAccess;
using ImageId = Common::SlotId; using ImageId = Common::SlotId;
@@ -66,7 +67,7 @@ public:
void CopyImage(Image& source); void CopyImage(Image& source);
void Resolve(Image& source, const ImageSubresourceRange& source_range, void Resolve(Image& source, const ImageSubresourceRange& source_range,
const ImageSubresourceRange& destination_range); const ImageSubresourceRange& destination_range);
void CopyImageWithBuffer(Image& source, Buffer& buffer); void CopyImageWithBuffer(Image& source, Buffer& buffer, TileManager& tiler);
void CopyMip(Image& source, uint32_t mip, uint32_t layer); void CopyMip(Image& source, uint32_t mip, uint32_t layer);
void InvalidateCpuWrite(uint64_t vaddr, uint64_t size) { void InvalidateCpuWrite(uint64_t vaddr, uint64_t size) {
@@ -15,6 +15,8 @@
namespace Libs::Graphics { namespace Libs::Graphics {
enum class ColorTransform : uint32_t { None, SwapBgra16, Reverse10_11_11 };
enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported }; enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported };
enum class ImageMetadataKind : uint8_t { None, Htile, Dcc, Cmask }; enum class ImageMetadataKind : uint8_t { None, Htile, Dcc, Cmask };
@@ -70,6 +72,14 @@ struct ImageInfo {
bool bgra16 = false; bool bgra16 = false;
std::array<ImageMipInfo, 16> mip_layout {}; std::array<ImageMipInfo, 16> mip_layout {};
[[nodiscard]] ColorTransform GetColorTransform() const noexcept {
if (bgra16) return ColorTransform::SwapBgra16;
switch (guest_format) {
case Prospero::BufferFormat::k10_11_11Float:
return ColorTransform::Reverse10_11_11;
default: return ColorTransform::None;
}
}
[[nodiscard]] constexpr bool HasStencil() const noexcept { return !stencil.Empty(); } [[nodiscard]] constexpr bool HasStencil() const noexcept { return !stencil.Empty(); }
[[nodiscard]] constexpr bool HasMetadata() const noexcept { [[nodiscard]] constexpr bool HasMetadata() const noexcept {
return metadata.kind != ImageMetadataKind::None; return metadata.kind != ImageMetadataKind::None;
@@ -91,7 +101,8 @@ struct ImageInfo {
} }
[[nodiscard]] bool IsCompatible(const ImageInfo& other) const noexcept { [[nodiscard]] bool IsCompatible(const ImageInfo& other) const noexcept {
return pixel_format == other.pixel_format && samples == other.samples && return pixel_format == other.pixel_format && samples == other.samples &&
bytes_per_block == other.bytes_per_block; bytes_per_block == other.bytes_per_block &&
GetColorTransform() == other.GetColorTransform();
} }
[[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept { [[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept {
if (container.resources.levels > container.mip_layout.size()) { if (container.resources.levels > container.mip_layout.size()) {
@@ -35,6 +35,9 @@ struct HostFormatInfo {
HostFormatInfo ResolveHostFormat(Prospero::BufferFormat guest_format, HostFormatInfo ResolveHostFormat(Prospero::BufferFormat guest_format,
Prospero::ChannelOrder order) { Prospero::ChannelOrder order) {
if (guest_format == Prospero::BufferFormat::k10_11_11Float) {
return {vk::Format::eB10G11R11UfloatPack32, Prospero::ColorMappingBgra};
}
if (order == Prospero::ChannelOrder::kAlt) { if (order == Prospero::ChannelOrder::kAlt) {
switch (guest_format) { switch (guest_format) {
case Prospero::BufferFormat::k8_8_8_8UNorm: case Prospero::BufferFormat::k8_8_8_8UNorm:
@@ -131,7 +134,8 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layo
if (host_format.format != vk::Format::eUndefined && bytes != 0) { if (host_format.format != vk::Format::eUndefined && bytes != 0) {
const auto order_mapping = const auto order_mapping =
kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u]; kRenderTargetColorMappings[static_cast<size_t>(order)][encoding.components - 1u];
return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping)}; return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping),
encoding.buffer_format};
} }
} }
EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n", EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n",
@@ -15,6 +15,7 @@ struct RenderTargetFormatInfo {
vk::Format format = vk::Format::eUndefined; vk::Format format = vk::Format::eUndefined;
uint32_t bytes_per_element = 0; uint32_t bytes_per_element = 0;
Prospero::ColorComponentMapping export_mapping; Prospero::ColorComponentMapping export_mapping;
Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid;
}; };
struct SurfaceFormatInfo { struct SurfaceFormatInfo {
+42 -36
View File
@@ -13,7 +13,7 @@
#include "gpu_tiler_shaders/gpu_tiler_standard4_spv.h" #include "gpu_tiler_shaders/gpu_tiler_standard4_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_standard64_3d_spv.h" #include "gpu_tiler_shaders/gpu_tiler_standard64_3d_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_standard64_spv.h" #include "gpu_tiler_shaders/gpu_tiler_standard64_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_swap_bgra16_spv.h" #include "gpu_tiler_shaders/gpu_tiler_color_transform_spv.h"
#include "graphics/host_gpu/graphicContext.h" #include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/cache/streamBuffer.h" #include "graphics/host_gpu/renderer/cache/streamBuffer.h"
#include "graphics/host_gpu/renderer/commandScheduler.h" #include "graphics/host_gpu/renderer/commandScheduler.h"
@@ -31,7 +31,7 @@ TileManager::TileManager(GraphicContext& graphics, CommandScheduler& scheduler,
StreamBuffer& stream_buffer) StreamBuffer& stream_buffer)
: m_graphics(graphics), m_scheduler(scheduler), m_stream_buffer(stream_buffer) { : m_graphics(graphics), m_scheduler(scheduler), m_stream_buffer(stream_buffer) {
static_assert(FamilyCount == 9); static_assert(FamilyCount == 9);
static_assert(sizeof(Push) == 52); static_assert(sizeof(Push) == 60);
std::array<vk::DescriptorSetLayoutBinding, 3> bindings {}; std::array<vk::DescriptorSetLayoutBinding, 3> bindings {};
for (uint32_t index = 0; index < 2; index++) { for (uint32_t index = 0; index < 2; index++) {
bindings[index] = {index, vk::DescriptorType::eStorageBuffer, 1, bindings[index] = {index, vk::DescriptorType::eStorageBuffer, 1,
@@ -77,8 +77,8 @@ TileManager::~TileManager() {
if (m_d32_to_d16 != nullptr) { if (m_d32_to_d16 != nullptr) {
m_graphics.device.destroyPipeline(m_d32_to_d16, nullptr); m_graphics.device.destroyPipeline(m_d32_to_d16, nullptr);
} }
if (m_swap_bgra16 != nullptr) { if (m_color_transform != nullptr) {
m_graphics.device.destroyPipeline(m_swap_bgra16, nullptr); m_graphics.device.destroyPipeline(m_color_transform, nullptr);
} }
if (m_pipeline_layout != nullptr) { if (m_pipeline_layout != nullptr) {
m_graphics.device.destroyPipelineLayout(m_pipeline_layout, nullptr); m_graphics.device.destroyPipelineLayout(m_pipeline_layout, nullptr);
@@ -90,7 +90,8 @@ TileManager::~TileManager() {
void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_capacity, void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_capacity,
std::span<const GpuTileInfo> infos, uint64_t source_base, std::span<const GpuTileInfo> infos, uint64_t source_base,
uint64_t target_base, std::vector<Dispatch>& dispatches) { uint64_t target_base, std::vector<Dispatch>& dispatches,
ColorTransform transform) {
EXIT_IF(infos.empty() || tiled_capacity == 0 || linear_capacity == 0); EXIT_IF(infos.empty() || tiled_capacity == 0 || linear_capacity == 0);
const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits; const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits;
EXIT_NOT_IMPLEMENTED(tiled_capacity > UINT32_MAX || linear_capacity > UINT32_MAX); EXIT_NOT_IMPLEMENTED(tiled_capacity > UINT32_MAX || linear_capacity > UINT32_MAX);
@@ -108,6 +109,8 @@ void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_ca
dispatches.clear(); dispatches.clear();
dispatches.reserve(infos.size()); dispatches.reserve(infos.size());
for (const auto& info: infos) { for (const auto& info: infos) {
EXIT_IF((transform == ColorTransform::SwapBgra16 && info.bytes_per_element != 8u) ||
(transform == ColorTransform::Reverse10_11_11 && info.bytes_per_element != 4u));
TileBlockLayout block {}; TileBlockLayout block {};
const uint32_t tiled_width = info.tiled_width != 0 ? info.tiled_width : info.pitch; const uint32_t tiled_width = info.tiled_width != 0 ? info.tiled_width : info.pitch;
const uint32_t tiled_height = info.tiled_height != 0 ? info.tiled_height : info.height; const uint32_t tiled_height = info.tiled_height != 0 ? info.tiled_height : info.height;
@@ -193,6 +196,7 @@ void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_ca
dispatch.push.tail_x = info.tail_x; dispatch.push.tail_x = info.tail_x;
dispatch.push.tail_y = info.tail_y; dispatch.push.tail_y = info.tail_y;
dispatch.push.tail = info.tail; dispatch.push.tail = info.tail;
dispatch.push.color_transform = static_cast<uint32_t>(transform);
dispatches.push_back(dispatch); dispatches.push_back(dispatch);
} }
@@ -338,13 +342,14 @@ void TileManager::Record(vk::Buffer source, uint64_t source_offset,
TileManager::Result TileManager::Detile(vk::Buffer tiled, uint64_t tiled_offset, TileManager::Result TileManager::Detile(vk::Buffer tiled, uint64_t tiled_offset,
uint64_t tiled_capacity, uint64_t linear_capacity, uint64_t tiled_capacity, uint64_t linear_capacity,
std::span<const GpuTileInfo> infos) { std::span<const GpuTileInfo> infos,
ColorTransform transform) {
const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits; const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits;
const uint64_t descriptor_alignment = const uint64_t descriptor_alignment =
std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4); std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4);
const uint64_t source_base = tiled_offset & (descriptor_alignment - 1); const uint64_t source_base = tiled_offset & (descriptor_alignment - 1);
std::vector<Dispatch> dispatches; std::vector<Dispatch> dispatches;
Prepare(false, tiled_capacity, linear_capacity, infos, source_base, 0, dispatches); Prepare(false, tiled_capacity, linear_capacity, infos, source_base, 0, dispatches, transform);
auto scratch = GetScratchBuffer(linear_capacity, tiled); auto scratch = GetScratchBuffer(linear_capacity, tiled);
Record(tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches, Record(tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches,
true); true);
@@ -375,15 +380,11 @@ void TileManager::TileImage(Image& image, std::span<const vk::BufferImageCopy> r
std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4); std::max<uint64_t>(limits.minStorageBufferOffsetAlignment, 4);
const uint64_t target_base = tiled_offset & (descriptor_alignment - 1); const uint64_t target_base = tiled_offset & (descriptor_alignment - 1);
std::vector<Dispatch> dispatches; std::vector<Dispatch> dispatches;
// Reserve stream parameters before recording the image download and conversion. // Reserve stream parameters before recording the image download.
Prepare(true, tiled_capacity, linear_capacity, infos, 0, target_base, dispatches); Prepare(true, tiled_capacity, linear_capacity, infos, 0, target_base, dispatches, transform);
auto linear = GetScratchBuffer(linear_capacity, tiled); auto linear = GetScratchBuffer(linear_capacity, tiled);
image.Download(regions, linear.buffer, 0, linear.size); image.Download(regions, linear.buffer, 0, linear.size);
Result source {linear.buffer, 0, linear.size}; Record(linear.buffer, 0, linear_capacity, tiled, tiled_offset, tiled_capacity,
if (transform == ColorTransform::SwapBgra16) {
source = SwapBgra16(source);
}
Record(source.buffer, source.offset, linear_capacity, tiled, tiled_offset, tiled_capacity,
dispatches, false); dispatches, false);
} }
@@ -582,9 +583,20 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio
&barriers[1], 0, nullptr); &barriers[1], 0, nullptr);
} }
void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) { void TileManager::TransformColor(Result input, Result output, ColorTransform transform,
if (m_swap_bgra16 == nullptr) { bool to_host) {
const auto module = CompileSPV(GPU_TILER_SWAP_BGRA16_SPV, m_graphics.device); uint32_t element_bytes = 0;
switch (transform) {
case ColorTransform::SwapBgra16: element_bytes = 8; break;
case ColorTransform::Reverse10_11_11: element_bytes = 4; break;
default: EXIT("invalid color transform\n");
}
EXIT_IF(input.size == 0 || input.size % element_bytes != 0 ||
input.size / element_bytes > UINT32_MAX ||
output.size < input.size);
const auto pixels = static_cast<uint32_t>(input.size / element_bytes);
if (m_color_transform == nullptr) {
const auto module = CompileSPV(GPU_TILER_COLOR_TRANSFORM_SPV, m_graphics.device);
vk::PipelineShaderStageCreateInfo stage {}; vk::PipelineShaderStageCreateInfo stage {};
stage.stage = vk::ShaderStageFlagBits::eCompute; stage.stage = vk::ShaderStageFlagBits::eCompute;
stage.module = module; stage.module = module;
@@ -593,14 +605,12 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
create.stage = stage; create.stage = stage;
create.layout = m_pipeline_layout; create.layout = m_pipeline_layout;
const auto result = const auto result =
m_graphics.device.createComputePipelines(nullptr, 1, &create, nullptr, &m_swap_bgra16); m_graphics.device.createComputePipelines(nullptr, 1, &create, nullptr, &m_color_transform);
m_graphics.device.destroyShaderModule(module, nullptr); m_graphics.device.destroyShaderModule(module, nullptr);
RequireVulkanSuccess(result, "create BGRA16 swap pipeline"); RequireVulkanSuccess(result, "create color transform pipeline");
} }
const uint64_t bytes = static_cast<uint64_t>(pixels) * 8u; const auto input_binding = BindStorage(input, input.size);
EXIT_IF(pixels == 0); const auto output_binding = BindStorage(output, input.size);
const auto input_binding = BindStorage(input, bytes);
const auto output_binding = BindStorage(output, bytes);
const vk::DescriptorBufferInfo infos[] { const vk::DescriptorBufferInfo infos[] {
input_binding.info, input_binding.info,
@@ -631,13 +641,15 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
command.pipelineBarrier( command.pipelineBarrier(
vk::PipelineStageFlagBits::eAllCommands | vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eAllCommands | vk::PipelineStageFlagBits::eHost,
vk::PipelineStageFlagBits::eComputeShader, {}, 0, nullptr, 2, barriers, 0, nullptr); vk::PipelineStageFlagBits::eComputeShader, {}, 0, nullptr, 2, barriers, 0, nullptr);
command.bindPipeline(vk::PipelineBindPoint::eCompute, m_swap_bgra16); command.bindPipeline(vk::PipelineBindPoint::eCompute, m_color_transform);
command.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, m_pipeline_layout, 0, command.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, m_pipeline_layout, 0,
static_cast<uint32_t>(writes.size()), writes.data()); static_cast<uint32_t>(writes.size()), writes.data());
Push push {}; Push push {};
push.src_base = input_binding.base; push.src_base = input_binding.base;
push.dst_base = output_binding.base; push.dst_base = output_binding.base;
push.width = pixels; push.width = pixels;
push.color_transform = static_cast<uint32_t>(transform);
push.to_host = to_host;
command.pushConstants(m_pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0, sizeof(push), command.pushConstants(m_pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0, sizeof(push),
&push); &push);
command.dispatch((pixels + 63u) / 64u, 1, 1); command.dispatch((pixels + 63u) / 64u, 1, 1);
@@ -648,17 +660,11 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) {
0, nullptr); 0, nullptr);
} }
TileManager::Result TileManager::SwapBgra16(Result input) { TileManager::Result TileManager::TransformColor(Result input, ColorTransform transform,
EXIT_NOT_IMPLEMENTED(input.size == 0 || input.size % 8u != 0 || input.size / 8u > UINT32_MAX); bool to_host) {
auto result = GetScratchBuffer(input.size, input.buffer); auto result = GetScratchBuffer(input.size, input.buffer);
SwapBgra16(input, result, static_cast<uint32_t>(input.size / 8u)); TransformColor(input, result, transform, to_host);
return result; return result;
} }
void TileManager::SwapBgra16(Result input, Result output) {
EXIT_NOT_IMPLEMENTED(input.size == 0 || input.size % 8u != 0 || input.size / 8u > UINT32_MAX ||
output.size < input.size);
SwapBgra16(input, output, static_cast<uint32_t>(input.size / 8u));
}
} // namespace Libs::Graphics } // namespace Libs::Graphics
+13 -11
View File
@@ -3,6 +3,7 @@
#include "common/common.h" #include "common/common.h"
#include "graphics/guest_gpu/tile.h" #include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/renderer/image/imageInfo.h"
#include "graphics/host_gpu/vulkanCommon.h" #include "graphics/host_gpu/vulkanCommon.h"
#include <array> #include <array>
@@ -42,7 +43,6 @@ struct GpuTileInfo {
class TileManager final { class TileManager final {
public: public:
enum class D16Direction { Promote, Demote }; enum class D16Direction { Promote, Demote };
enum class ColorTransform { None, SwapBgra16 };
struct Result { struct Result {
vk::Buffer buffer = nullptr; vk::Buffer buffer = nullptr;
@@ -65,7 +65,8 @@ public:
// Consume scratch results before the next acquisition, or pass their buffer as input. // Consume scratch results before the next acquisition, or pass their buffer as input.
[[nodiscard]] Result Detile(vk::Buffer tiled, uint64_t tiled_offset, uint64_t tiled_capacity, [[nodiscard]] Result Detile(vk::Buffer tiled, uint64_t tiled_offset, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const GpuTileInfo> infos); uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
ColorTransform transform = ColorTransform::None);
void Tile(vk::Buffer linear, uint64_t linear_offset, uint64_t linear_capacity, vk::Buffer tiled, void Tile(vk::Buffer linear, uint64_t linear_offset, uint64_t linear_capacity, vk::Buffer tiled,
uint64_t tiled_offset, uint64_t tiled_capacity, std::span<const GpuTileInfo> infos); uint64_t tiled_offset, uint64_t tiled_capacity, std::span<const GpuTileInfo> infos);
void TileImage(Image& image, std::span<const vk::BufferImageCopy> regions, vk::Buffer tiled, void TileImage(Image& image, std::span<const vk::BufferImageCopy> regions, vk::Buffer tiled,
@@ -75,8 +76,8 @@ public:
[[nodiscard]] Result GetScratchBuffer(uint64_t size, vk::Buffer input = nullptr); [[nodiscard]] Result GetScratchBuffer(uint64_t size, vk::Buffer input = nullptr);
void ConvertD16(Result source, Result target, D16Direction direction, bool d32, void ConvertD16(Result source, Result target, D16Direction direction, bool d32,
const D16Layout& layout); const D16Layout& layout);
[[nodiscard]] Result SwapBgra16(Result input); [[nodiscard]] Result TransformColor(Result input, ColorTransform transform, bool to_host);
void SwapBgra16(Result input, Result output); void TransformColor(Result input, Result output, ColorTransform transform, bool to_host);
private: private:
friend struct TileManagerTestAccess; friend struct TileManagerTestAccess;
@@ -100,6 +101,8 @@ private:
uint32_t tail_x; uint32_t tail_x;
uint32_t tail_y; uint32_t tail_y;
uint32_t tail; uint32_t tail;
uint32_t color_transform;
uint32_t to_host;
}; };
struct Dispatch { struct Dispatch {
Push push {}; Push push {};
@@ -118,12 +121,11 @@ private:
uint32_t max_groups) noexcept; uint32_t max_groups) noexcept;
void Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_capacity, void Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_capacity,
std::span<const GpuTileInfo> infos, uint64_t source_base, uint64_t target_base, std::span<const GpuTileInfo> infos, uint64_t source_base, uint64_t target_base,
std::vector<Dispatch>& dispatches); std::vector<Dispatch>& dispatches, ColorTransform transform = ColorTransform::None);
void Record(vk::Buffer source, uint64_t source_offset, uint64_t source_capacity, void Record(vk::Buffer source, uint64_t source_offset, uint64_t source_capacity,
vk::Buffer target, uint64_t target_offset, uint64_t target_capacity, vk::Buffer target, uint64_t target_offset, uint64_t target_capacity,
std::span<Dispatch> dispatches, bool clear_target); std::span<Dispatch> dispatches, bool clear_target);
[[nodiscard]] vk::Pipeline GetPipeline(uint32_t slot); [[nodiscard]] vk::Pipeline GetPipeline(uint32_t slot);
void SwapBgra16(Result input, Result output, uint32_t pixels);
GraphicContext& m_graphics; GraphicContext& m_graphics;
CommandScheduler& m_scheduler; CommandScheduler& m_scheduler;
@@ -132,11 +134,11 @@ private:
vk::DescriptorSetLayout m_descriptor_layout = nullptr; vk::DescriptorSetLayout m_descriptor_layout = nullptr;
vk::PipelineLayout m_pipeline_layout = nullptr; vk::PipelineLayout m_pipeline_layout = nullptr;
std::array<vk::Pipeline, PipelineCount> m_pipelines {}; std::array<vk::Pipeline, PipelineCount> m_pipelines {};
vk::Pipeline m_d16_to_d24 = nullptr; vk::Pipeline m_d16_to_d24 = nullptr;
vk::Pipeline m_d16_to_d32 = nullptr; vk::Pipeline m_d16_to_d32 = nullptr;
vk::Pipeline m_d24_to_d16 = nullptr; vk::Pipeline m_d24_to_d16 = nullptr;
vk::Pipeline m_d32_to_d16 = nullptr; vk::Pipeline m_d32_to_d16 = nullptr;
vk::Pipeline m_swap_bgra16 = nullptr; vk::Pipeline m_color_transform = nullptr;
}; };
} // namespace Libs::Graphics } // namespace Libs::Graphics
@@ -0,0 +1,20 @@
// ColorTransform values match imageInfo.h. Preserve the floating-point bit fields exactly.
uvec2 transform_color(uvec2 value, uint transform, bool to_host) {
switch (transform) {
case 1u:
return uvec2((value.y & 0xffffu) | (value.x & 0xffff0000u),
(value.x & 0xffffu) | (value.y & 0xffff0000u));
case 2u: {
uint packed = value.x;
if (to_host) {
value.x = (packed >> 21u) | (((packed >> 10u) & 0x7ffu) << 11u) |
((packed & 0x3ffu) << 22u);
} else {
value.x = (packed >> 22u) | (((packed >> 11u) & 0x7ffu) << 10u) |
((packed & 0x7ffu) << 21u);
}
break;
}
}
return value;
}
@@ -1,4 +1,5 @@
#version 450 #version 450
#extension GL_GOOGLE_include_directive : require
layout(local_size_x = 64) in; layout(local_size_x = 64) in;
@@ -19,17 +20,23 @@ layout(push_constant, std430) uniform Push {
uint tail_x; uint tail_x;
uint tail_y; uint tail_y;
uint tail; uint tail;
uint color_transform;
uint to_host;
} params; } params;
#include "gpu_tiler_color.inc"
void main() { void main() {
uint pixel = gl_GlobalInvocationID.x; uint pixel = gl_GlobalInvocationID.x;
if (pixel >= params.width) { if (pixel >= params.width) {
return; return;
} }
uint src = (params.src_base >> 2u) + pixel * 2u; uint words = params.color_transform == 1u ? 2u : 1u;
uint dst = (params.dst_base >> 2u) + pixel * 2u; uint src = (params.src_base >> 2u) + pixel * words;
uint bg = input_buffer.data[src]; uint dst = (params.dst_base >> 2u) + pixel * words;
uint ra = input_buffer.data[src + 1u]; uvec2 value = uvec2(input_buffer.data[src], 0u);
output_buffer.data[dst] = (ra & 0x0000ffffu) | (bg & 0xffff0000u); if (words == 2u) value.y = input_buffer.data[src + 1u];
output_buffer.data[dst + 1u] = (bg & 0x0000ffffu) | (ra & 0xffff0000u); value = transform_color(value, params.color_transform, params.to_host != 0u);
output_buffer.data[dst] = value.x;
if (words == 2u) output_buffer.data[dst + 1u] = value.y;
} }
@@ -17,10 +17,19 @@ layout(set = 0, binding = 2, std140) uniform Push {
uint tail_x; uint tail_x;
uint tail_y; uint tail_y;
uint tail; uint tail;
uint color_transform;
} params; } params;
#include "gpu_tiler_color.inc"
void copy_element(uint src, uint dst) { void copy_element(uint src, uint dst) {
if (ELEMENT_BYTES >= 4) { if ((ELEMENT_BYTES == 4u || ELEMENT_BYTES == 8u) && params.color_transform != 0u) {
uvec2 value = uvec2(input_buffer.data[src >> 2], 0u);
if (ELEMENT_BYTES == 8u) value.y = input_buffer.data[(src >> 2) + 1u];
value = transform_color(value, params.color_transform, TILE == 0u);
output_buffer.data[dst >> 2] = value.x;
if (ELEMENT_BYTES == 8u) output_buffer.data[(dst >> 2) + 1u] = value.y;
} else if (ELEMENT_BYTES >= 4) {
for (uint i = 0; i < ELEMENT_BYTES; i += 4) { for (uint i = 0; i < ELEMENT_BYTES; i += 4) {
output_buffer.data[(dst + i) >> 2] = input_buffer.data[(src + i) >> 2]; output_buffer.data[(dst + i) >> 2] = input_buffer.data[(src + i) >> 2];
} }
+160 -10
View File
@@ -8848,6 +8848,8 @@ public:
total.size >= 8 && total.size <= allocation_size, total.size >= 8 && total.size <= allocation_size,
"BGRA16 tiled layout exceeds its guest allocation"); "BGRA16 tiled layout exceeds its guest allocation");
std::memset(mapped, 0x5a, total.size); std::memset(mapped, 0x5a, total.size);
constexpr std::array<uint16_t, 4> initial{0x3c00u, 0x4000u, 0x4200u, 0x4400u};
std::memcpy(mapped, initial.data(), sizeof(initial));
ImageDesc desc{}; ImageDesc desc{};
desc.type = BindingType::RenderTarget; desc.type = BindingType::RenderTarget;
@@ -8870,6 +8872,11 @@ public:
auto &cache = resources.GetTextureCache(); auto &cache = resources.GetTextureCache();
const auto id = cache.FindImage(desc); const auto id = cache.FindImage(desc);
(void)cache.FindRenderTarget(id, desc);
Require(name, "tiled BGRA16 upload",
ReadCachedTexel(name, context, id) ==
std::vector<u32>{0x40004200u, 0x44003c00u},
"fused detiling lost the BGRA16 component transform");
auto &image = cache.GetImage(id); auto &image = cache.GetImage(id);
image.Transit(vk::ImageLayout::eTransferDstOptimal, image.Transit(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite, {}, vk::AccessFlagBits2::eTransferWrite, {},
@@ -10480,6 +10487,15 @@ public:
target.format == vk::Format::eR5G5B5A1UnormPack16 && target.format == vk::Format::eR5G5B5A1UnormPack16 &&
target.export_mapping == Prospero::ColorMappingAbgr, target.export_mapping == Prospero::ColorMappingAbgr,
"shared format resolution changed the existing 1555 render target"); "shared format resolution changed the existing 1555 render target");
for (const auto native : {target, TextureGetRenderTargetFormat(
Prospero::ChannelLayout::k8, Prospero::ChannelType::kSNorm,
Prospero::ChannelOrder::kStandard)}) {
const auto upload = TextureCalcUploadLayout(native.guest_format, 4, 1, 1, 1,
Prospero::TileMode::kLinear, 256, false, name);
Require(name, "render-target guest upload layout",
upload.pitch * native.bytes_per_element == 256 && upload.mips[0].size == 256,
"retaining the render-target format lost its physical byte width");
}
EnsureRuntimeContext(); EnsureRuntimeContext();
int64_t direct_offset = -1; int64_t direct_offset = -1;
Require(name, "allocation", Require(name, "allocation",
@@ -10568,6 +10584,134 @@ public:
resources.UnmapMemory(base, allocation_size); resources.UnmapMemory(base, allocation_size);
scheduler.Finish(); scheduler.Finish();
} }
for (const auto tile : {Prospero::TileMode::kLinear, Prospero::TileMode::kRenderTarget}) {
constexpr std::array<u32, 4> words{0x883005e1u, 0x7c0e81c0u,
0x800f0000u, 0x98120280u};
constexpr std::array<std::array<float, 4>, 4> channels{{
{1.03125f, 2.03125f, 4.0625f, 1.f}, {0.5f, 0.75f, 1.5f, 1.f},
{0.f, 1.f, 2.f, 1.f}, {32.f, 8.f, 16.f, 1.f}}};
const auto target = TextureGetRenderTargetFormat(
Prospero::ChannelLayout::k10_11_11, Prospero::ChannelType::kFloat,
Prospero::ChannelOrder::kReversed);
Require(name, "10_11_11 reversed render target",
target.format == vk::Format::eB10G11R11UfloatPack32 &&
target.guest_format == Prospero::BufferFormat::k10_11_11Float &&
target.export_mapping == Prospero::ColorMappingRgba,
"PPSA24156 reversed packed-float target lost its physical encoding");
std::memset(mapped, 0, allocation_size);
std::array<u32, 4> offsets{0, 4, 8, 12};
if (tile == Prospero::TileMode::kRenderTarget) {
TileBlockLayout block{};
u32 block_xor = 0;
Require(name, "10_11_11 tile layout",
TileGetBlockLayout(TileBlockFamily::RenderTarget64KB, 4, block) &&
TileGetBlockXor(block, 0, 0, 0, block_xor),
"packed-float render-target layout is unavailable");
for (u32 pixel = 0; pixel < words.size(); ++pixel) {
Require(name, "10_11_11 tile offset",
TileGetBlockOffset(block, pixel, 0, 0, offsets[pixel]),
"packed-float texel offset is unavailable");
offsets[pixel] ^= block_xor;
}
}
for (u32 pixel = 0; pixel < words.size(); ++pixel) {
std::memcpy(static_cast<uint8_t *>(mapped) + offsets[pixel], &words[pixel], sizeof(u32));
}
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
context.MapMemory(base, allocation_size);
auto &cache = context.GetTextureCache();
auto &executor = context.GetRenderExecutor();
constexpr u32 swizzle = DstSel(4, 5, 6, 1);
ShaderTextureResource descriptor{{static_cast<u32>(base >> 8u),
(static_cast<u32>(Prospero::BufferFormat::k10_11_11Float) << 20u) |
(3u << 30u), 0,
swizzle | (static_cast<u32>(tile) << 20u) |
(static_cast<u32>(Prospero::ImageType::kColor2D) << 28u),
0, 0x00700000u, 0, 0}};
TestCase test;
test.name = name;
test.has_user_data = true;
test.image_descriptor_swizzle = swizzle;
std::copy_n(descriptor.fields, 8, test.user_data.begin());
test.user_data[50] = 16 * sizeof(u32);
for (u32 pixel = 0; pixel < words.size(); ++pixel) {
AppendVMovU32(&test.code, 20, pixel);
AppendVMovU32(&test.code, 21, 0);
test.code.push_back(EncodeMimg0(0x00, 0xf));
test.code.push_back(EncodeMimg1(0, 20));
for (u32 channel = 0; channel < 4; ++channel) {
AppendStoreVgpr(&test.code, channel, pixel * 4 + channel);
test.expected.push_back(std::bit_cast<u32>(channels[pixel][channel]));
}
}
AppendEnd(&test.code);
const auto compiled = CompileCase(test, SubgroupSize());
ShaderRecompiler::IR::DescriptorValue value{};
value.dword_count = 8;
std::copy_n(descriptor.fields, 8, value.dwords.begin());
const auto binding = RenderExecutorTestAccess::ResolveTexture(
executor, compiled.program.info.images.at(0), value);
Image sampled;
sampled.view = cache.FindTexture(binding.image_id, binding.desc);
sampled.layout = cache.GetImage(binding.image_id).backing.state.layout;
scheduler.Finish();
auto output = CreateStorageBuffer(name, {}, 16);
Dispatch(test, compiled, output, nullptr, &sampled);
Require(name, "10_11_11 uploaded components",
ReadBuffer(name, output, 16) == test.expected,
"packed-float upload or sample changed a component's width or order");
DestroyBuffer(&output);
const auto clear = [&](std::array<float, 4> channels) {
auto &native = cache.GetImage(binding.image_id);
native.Transit(vk::ImageLayout::eTransferDstOptimal,
vk::AccessFlagBits2::eTransferWrite, {},
scheduler.Current().Handle());
const vk::ImageSubresourceRange range{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
scheduler.Current().Handle().clearColorImage(native.backing.image,
vk::ImageLayout::eTransferDstOptimal, vk::ClearColorValue{channels}, range);
cache.MarkGpuWritten(binding.image_id);
};
clear({8.f, 4.f, 2.f, 1.f});
Require(name, "10_11_11 guest download",
TextureCacheTestAccess::TryDownload(cache, binding.image_id),
"packed-float native contents could not be downloaded");
scheduler.Finish();
scheduler.DrainPriorityOperations();
std::array<u32, 4> guest{};
for (u32 pixel = 0; pixel < words.size(); ++pixel) {
Require(name, "10_11_11 guest texel",
Libs::LibKernel::Memory::TryReadBacking(base + offsets[pixel],
&guest[pixel], sizeof(u32)),
"packed-float guest texel is inaccessible");
}
Require(name, "10_11_11 exact guest words",
guest == std::array<u32, 4>{0x90110200u, 0x90110200u, 0x90110200u, 0x90110200u},
"native packed-float write lost the guest 10/11/11 bit layout");
clear({16.f, 8.f, 4.f, 1.f});
auto alias_desc = binding.desc;
alias_desc.info.guest_format = Prospero::BufferFormat::k11_11_10Float;
alias_desc.view_info.mapping = {};
Require(name, "same-format transform compatibility",
!alias_desc.info.IsCompatible(binding.desc.info),
"equal Vulkan formats hid incompatible guest color transforms");
const auto alias = cache.FindImage(alias_desc);
(void)cache.FindTexture(alias, alias_desc);
Require(name, "same-format transformed alias",
alias != binding.image_id &&
ReadCachedTexel(name, context, alias, {}, {4, 1, 1}) ==
std::vector<u32>(4, 0x98120220u),
"packed-float alias reused a transformed image or bypassed guest bytes");
RenderExecutorTestAccess::ResetBindings(executor);
context.UnmapMemory(base, allocation_size);
scheduler.Finish();
}
Require(name, "release", Require(name, "release",
Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0 && Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0 &&
Libs::LibKernel::Memory::KernelReleaseDirectMemory( Libs::LibKernel::Memory::KernelReleaseDirectMemory(
@@ -15980,13 +16124,16 @@ public:
}; };
// Keep all four conversions in flight to check growth, overwrite ordering, // Keep all four conversions in flight to check growth, overwrite ordering,
// the requested span on reuse, and two simultaneously live workspaces. // the requested span on reuse, and two simultaneously live workspaces.
const auto first = tile_manager.SwapBgra16({scratch_input.buffer, 0, 8}); const auto first = tile_manager.TransformColor(
{scratch_input.buffer, 0, 8}, ColorTransform::SwapBgra16, true);
copy_scratch(first, 0); copy_scratch(first, 0);
const auto grown = tile_manager.SwapBgra16({scratch_input.buffer, 0, 32}); const auto grown = tile_manager.TransformColor(
{scratch_input.buffer, 0, 32}, ColorTransform::SwapBgra16, true);
copy_scratch(grown, 8); copy_scratch(grown, 8);
const auto reused = tile_manager.SwapBgra16({scratch_input.buffer, 8, 8}); const auto reused = tile_manager.TransformColor(
{scratch_input.buffer, 8, 8}, ColorTransform::SwapBgra16, true);
copy_scratch(reused, 40); copy_scratch(reused, 40);
const auto paired = tile_manager.SwapBgra16(reused); const auto paired = tile_manager.TransformColor(reused, ColorTransform::SwapBgra16, true);
copy_scratch(paired, 48); copy_scratch(paired, 48);
Require(name, "scratch workspace reuse", Require(name, "scratch workspace reuse",
first.buffer != grown.buffer && grown.buffer == reused.buffer && first.buffer != grown.buffer && grown.buffer == reused.buffer &&
@@ -35223,7 +35370,9 @@ void CheckImageTransitionState(RenderContext &renderer) {
buffered_upload_offset, buffered_expected.size()); buffered_upload_offset, buffered_expected.size());
Buffer copy_scratch(context, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, Buffer copy_scratch(context, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags,
16); 16);
buffered_destination.CopyImageWithBuffer(buffered_source, copy_scratch); StreamBuffer copy_parameters(context, scheduler, MemoryUsage::Stream, 4096);
TileManager copy_tiler(context, scheduler, copy_parameters);
buffered_destination.CopyImageWithBuffer(buffered_source, copy_scratch, copy_tiler);
const auto [buffered_download_data, buffered_download_offset] = const auto [buffered_download_data, buffered_download_offset] =
download.Map(buffered_expected.size(), 4); download.Map(buffered_expected.size(), 4);
Require(name, "buffered-copy download map", buffered_download_data != nullptr, Require(name, "buffered-copy download map", buffered_download_data != nullptr,
@@ -38898,6 +39047,7 @@ int main(int argc, char **argv) {
if (argc == 2 && std::strcmp(argv[1], "--packed-texture-only") == 0) { if (argc == 2 && std::strcmp(argv[1], "--packed-texture-only") == 0) {
VulkanHarness vulkan; VulkanHarness vulkan;
vulkan.CheckPackedTextureComponents(); vulkan.CheckPackedTextureComponents();
vulkan.CheckBgra16Readback();
RunCase(&vulkan, ImageLoadPackedUintUnpacksAndSwizzles()); RunCase(&vulkan, ImageLoadPackedUintUnpacksAndSwizzles());
RunCase(&vulkan, ImageSamplePackedUintConvertsSampleAndGather()); RunCase(&vulkan, ImageSamplePackedUintConvertsSampleAndGather());
return 0; return 0;
@@ -39374,6 +39524,11 @@ int main(int argc, char **argv) {
vulkan.CheckRenderExecutorColor1DArrayDiscovery(); vulkan.CheckRenderExecutorColor1DArrayDiscovery();
return 0; return 0;
} }
if (argc == 2 && std::strcmp(argv[1], "--image-transition-only") == 0) {
VulkanHarness vulkan;
CheckImageTransitionState(vulkan.RuntimeRenderer());
return 0;
}
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-death") == 0) { if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-death") == 0) {
RunReverseRenderTargetDeathCase(); RunReverseRenderTargetDeathCase();
@@ -39396,11 +39551,6 @@ int main(int argc, char **argv) {
CheckSampledColorViews(); CheckSampledColorViews();
return 0; return 0;
} }
if (argc == 2 && std::strcmp(argv[1], "--image-transition-only") == 0) {
VulkanHarness vulkan;
CheckImageTransitionState(vulkan.RuntimeRenderer());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--storage-bgra-only") == 0) { if (argc == 2 && std::strcmp(argv[1], "--storage-bgra-only") == 0) {
CheckSampledColorViews(); CheckSampledColorViews();
CheckBasicStorageTextureDescriptor(); CheckBasicStorageTextureDescriptor();