Add GPU tilers/tests

This commit is contained in:
nmzik
2026-07-21 07:37:52 +02:00
parent 728b5b7d01
commit b51d4322cc
31 changed files with 3071 additions and 3360 deletions
+34
View File
@@ -170,6 +170,40 @@ file(GLOB kyty_emulator_src CONFIGURE_DEPENDS
loader/*.h
)
find_program(KYTY_GLSLANG_VALIDATOR glslangValidator REQUIRED)
set(gpu_tiler_shader_dir "${CMAKE_CURRENT_SOURCE_DIR}/graphics/host_gpu/shaders")
set(gpu_tiler_generated_dir "${PROJECT_BINARY_DIR}/gpu_tiler_shaders")
set(gpu_tiler_shader_names
standard256
standard4
standard4_3d
standard64
standard64_3d
prt
prt_3d
render_target
depth
)
file(GLOB gpu_tiler_shader_includes CONFIGURE_DEPENDS "${gpu_tiler_shader_dir}/gpu_tiler_*.inc")
foreach(shader_name IN LISTS gpu_tiler_shader_names)
set(shader_source "${gpu_tiler_shader_dir}/gpu_tiler_${shader_name}.comp")
set(shader_spv "${gpu_tiler_generated_dir}/gpu_tiler_${shader_name}.spv")
set(shader_header "${gpu_tiler_generated_dir}/gpu_tiler_${shader_name}_spv.h")
string(TOUPPER "GPU_TILER_${shader_name}_SPV" shader_symbol)
add_custom_command(
OUTPUT "${shader_header}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${gpu_tiler_generated_dir}"
COMMAND "${KYTY_GLSLANG_VALIDATOR}" -V --target-env vulkan1.0 -Os
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
-DSYMBOL=${shader_symbol} -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${shader_source}" ${gpu_tiler_shader_includes}
VERBATIM
)
list(APPEND gpu_tiler_shader_headers "${shader_header}")
endforeach()
list(APPEND kyty_emulator_src ${gpu_tiler_shader_headers})
list(APPEND kyty_emulator_src
emulator.h
emulator.cpp
+30
View File
@@ -0,0 +1,30 @@
if(NOT DEFINED INPUT OR NOT DEFINED OUTPUT OR NOT DEFINED SYMBOL)
message(FATAL_ERROR "embed_spirv.cmake requires INPUT, OUTPUT, and SYMBOL")
endif()
file(READ "${INPUT}" bytes HEX)
string(LENGTH "${bytes}" length)
math(EXPR remainder "${length} % 8")
if(NOT remainder EQUAL 0)
message(FATAL_ERROR "SPIR-V byte count is not a multiple of four: ${INPUT}")
endif()
set(contents "#pragma once\n#include <cstdint>\ninline constexpr uint32_t ${SYMBOL}[] = {\n")
set(column 0)
while(length GREATER 0)
string(SUBSTRING "${bytes}" 0 8 word)
string(SUBSTRING "${word}" 0 2 b0)
string(SUBSTRING "${word}" 2 2 b1)
string(SUBSTRING "${word}" 4 2 b2)
string(SUBSTRING "${word}" 6 2 b3)
string(APPEND contents "0x${b3}${b2}${b1}${b0}u,")
math(EXPR column "${column} + 1")
if(column EQUAL 8)
string(APPEND contents "\n")
set(column 0)
endif()
string(SUBSTRING "${bytes}" 8 -1 bytes)
math(EXPR length "${length} - 8")
endwhile()
string(APPEND contents "\n};\n")
file(WRITE "${OUTPUT}" "${contents}")
File diff suppressed because it is too large Load Diff
+49 -56
View File
@@ -6,15 +6,6 @@
namespace Libs::Graphics {
enum class TileMode {
VideoOutLinear,
VideoOutTiled,
TextureLinear,
TextureTiled,
// RenderTextureLinear,
// RenderTextureTiled,
};
struct TileSizeAlign {
uint32_t size = 0;
uint32_t align = 0;
@@ -34,51 +25,58 @@ struct TilePaddedSize {
uint32_t height = 0;
};
void TileInit();
void TileConvertTiledToLinear(void* dst, const void* src, TileMode mode, uint32_t width,
uint32_t height);
void TileConvertTiledToLinearRenderTarget(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, uint64_t size,
uint64_t src_size = 0, uint32_t src_x = 0,
uint32_t src_y = 0);
void TileConvertLinearToTiledRenderTarget(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch,
uint32_t bytes_per_element, uint64_t size,
uint64_t dst_size = 0, uint32_t dst_x = 0,
uint32_t dst_y = 0);
void TileConvertTiledToLinearStandard64KB(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t pitch,
uint64_t size, uint64_t src_size = 0, uint32_t src_x = 0,
uint32_t src_y = 0);
void TileConvertTiledToLinearStandard64KB32(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size,
uint64_t src_size = 0, uint32_t src_x = 0,
uint32_t src_y = 0);
void TileConvertLinearToTiledStandard64KB32(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size);
void TileConvertTiledToLinearStandard64KB16(void* dst, const void* src, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size,
uint64_t src_size = 0, uint32_t src_x = 0,
uint32_t src_y = 0);
void TileConvertTiledToLinearDepth(void* dst, const void* src, uint32_t format, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size);
void TileConvertLinearToTiledDepth(void* dst, const void* src, uint32_t format, uint32_t width,
uint32_t height, uint32_t pitch, uint64_t size);
void TileConvertTiledToLinearStandard4KB(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t pitch,
uint64_t dst_size, uint64_t src_size, uint32_t src_x = 0,
uint32_t src_y = 0);
void TileConvertTiledToLinearStandard256B(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t pitch,
uint64_t dst_size, uint64_t src_size);
enum class TileBlockFamily : uint32_t {
Standard256B,
Standard4KB,
Standard4KB3D,
Standard64KB,
Standard64KB3D,
Prt64KB,
Prt64KB3D,
RenderTarget64KB,
Depth64KB,
Count,
};
struct TileBlockLayout {
TileBlockFamily family = TileBlockFamily::Standard256B;
uint32_t bytes_per_element = 0;
uint32_t block_size = 0;
uint32_t block_width = 0;
uint32_t block_height = 0;
uint32_t block_depth = 0;
};
struct TileVolumeLayout {
TileBlockFamily family = TileBlockFamily::Count;
uint32_t bytes_per_element = 0;
uint32_t texel_width = 1;
uint32_t texel_height = 1;
uint32_t first_tail_level = 0;
uint32_t block_depth = 1;
uint64_t block_slice_size = 0;
uint64_t total_size = 0;
uint64_t level_offsets[16] = {};
uint64_t level_sizes[16] = {};
uint32_t tail_x[16] = {};
uint32_t tail_y[16] = {};
uint32_t level_widths[16] = {};
uint32_t level_heights[16] = {};
};
bool TileGetBlockLayout(TileBlockFamily family, uint32_t bytes_per_element,
TileBlockLayout* layout);
bool TileGetBlockOffset(const TileBlockLayout& layout, uint32_t x, uint32_t y, uint32_t z,
uint32_t* byte_offset);
bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t block_y,
uint32_t* byte_offset);
bool TileGetBlockXor(const TileBlockLayout& layout, uint32_t block_x, uint32_t block_y,
uint32_t block_z, uint32_t* byte_offset);
bool TileIsStandard256BTextureSupported(uint32_t format);
bool TileIsStandard4KBTextureSupported(uint32_t format);
bool TileIsStandard64KBTextureSupported(uint32_t format);
bool TileGetStandard4KBVolumeLayout(uint32_t format, uint32_t* bytes_per_element,
uint32_t* texels_per_element_wide,
uint32_t* texels_per_element_tall, uint32_t* block_width_log2,
uint32_t* block_height_log2, uint32_t* block_depth_log2);
bool TileGetTextureVolumeLayout(uint32_t format, uint32_t width, uint32_t height, uint32_t depth,
uint32_t levels, uint32_t tile, TileVolumeLayout* layout);
bool TileGetHtileSize(uint32_t width, uint32_t height, TileSizeAlign* htile_size);
bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t z_format,
@@ -103,11 +101,6 @@ void TileGetTextureTotalSize(uint32_t format, uint32_t width, uint32_t height, u
uint32_t pitch, uint32_t levels, uint32_t tile, bool volume_texture,
TileSizeAlign* total_size);
uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t levels, uint32_t tile);
void TileConvertTiledToLinearStandard4KB3D(void* dst, const void* src, uint32_t format,
uint32_t width, uint32_t height, uint32_t depth,
uint32_t pitch, uint64_t dst_slice_stride,
uint64_t dst_size, uint64_t src_size,
bool clear_dst = true);
} // namespace Libs::Graphics
+547
View File
@@ -0,0 +1,547 @@
#include "graphics/host_gpu/gpuTiler.h"
#include "common/assert.h"
#include "common/threads.h"
#include "gpu_tiler_shaders/gpu_tiler_depth_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_prt_3d_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_prt_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_render_target_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_standard256_spv.h"
#include "gpu_tiler_shaders/gpu_tiler_standard4_3d_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_spv.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vma.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <algorithm>
#include <array>
#include <bit>
#include <cstring>
#include <vector>
namespace Libs::Graphics {
namespace {
constexpr uint32_t GROUP_SIZE = 64;
constexpr uint32_t FAMILY_COUNT = static_cast<uint32_t>(TileBlockFamily::Count);
constexpr uint32_t BYTES_PER_ELEMENT_COUNT = 5;
constexpr uint32_t DIRECTION_COUNT = 2;
constexpr uint32_t PIPELINE_COUNT = FAMILY_COUNT * BYTES_PER_ELEMENT_COUNT * DIRECTION_COUNT;
static_assert(FAMILY_COUNT == 9);
struct Push {
uint32_t src_base;
uint32_t dst_base;
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t surface_z;
uint32_t pitch_bytes;
uint32_t slice_bytes;
uint32_t blocks_per_row;
uint32_t blocks_per_slice;
uint32_t tail_x;
uint32_t tail_y;
uint32_t tail;
uint32_t first;
uint32_t count;
};
static_assert(sizeof(Push) == 60);
struct Shader {
const uint32_t* code;
size_t words;
};
constexpr std::array<Shader, FAMILY_COUNT> SHADERS {{
{GPU_TILER_STANDARD256_SPV, std::size(GPU_TILER_STANDARD256_SPV)},
{GPU_TILER_STANDARD4_SPV, std::size(GPU_TILER_STANDARD4_SPV)},
{GPU_TILER_STANDARD4_3D_SPV, std::size(GPU_TILER_STANDARD4_3D_SPV)},
{GPU_TILER_STANDARD64_SPV, std::size(GPU_TILER_STANDARD64_SPV)},
{GPU_TILER_STANDARD64_3D_SPV, std::size(GPU_TILER_STANDARD64_3D_SPV)},
{GPU_TILER_PRT_SPV, std::size(GPU_TILER_PRT_SPV)},
{GPU_TILER_PRT_3D_SPV, std::size(GPU_TILER_PRT_3D_SPV)},
{GPU_TILER_RENDER_TARGET_SPV, std::size(GPU_TILER_RENDER_TARGET_SPV)},
{GPU_TILER_DEPTH_SPV, std::size(GPU_TILER_DEPTH_SPV)},
}};
struct Dispatch {
Push push {};
uint32_t pipeline_slot = 0;
uint32_t elements = 0;
};
struct Resources {
vk::DescriptorSetLayout descriptor_layout = nullptr;
vk::PipelineLayout pipeline_layout = nullptr;
vk::DescriptorPool descriptor_pool = nullptr;
vk::DescriptorSet descriptor_set = nullptr;
std::array<vk::Pipeline, PIPELINE_COUNT> pipelines {};
VulkanBuffer staging;
VulkanBuffer linear;
void* mapped = nullptr;
};
bool CheckedAdd(uint64_t a, uint64_t b, uint64_t* result) {
return b <= UINT64_MAX - a && (*result = a + b, true);
}
bool CheckedMultiply(uint64_t a, uint64_t b, uint64_t* result) {
return (a == 0 || b <= UINT64_MAX / a) && (*result = a * b, true);
}
bool CheckedAddProduct(uint64_t* value, uint64_t count, uint64_t stride) {
uint64_t bytes = 0;
return CheckedMultiply(count, stride, &bytes) && CheckedAdd(*value, bytes, value);
}
bool IsRangeValid(uint64_t offset, uint64_t size, uint64_t capacity) {
return size != 0 && offset <= capacity && size <= capacity - offset;
}
uint64_t AlignToDword(uint64_t value) {
return (value + 3u) & ~uint64_t {3};
}
uint32_t GetPipelineSlot(bool to_tiled, TileBlockFamily family, uint32_t bytes_per_element) {
const uint32_t direction_index = to_tiled ? 1u : 0u;
const uint32_t family_index = static_cast<uint32_t>(family);
const uint32_t element_size_index = std::countr_zero(bytes_per_element);
return (direction_index * FAMILY_COUNT + family_index) * BYTES_PER_ELEMENT_COUNT +
element_size_index;
}
void Barrier(vk::CommandBuffer command, vk::Buffer buffer, vk::AccessFlags src_access,
vk::AccessFlags dst_access, vk::PipelineStageFlags src_stage,
vk::PipelineStageFlags dst_stage) {
vk::BufferMemoryBarrier barrier {};
barrier.sType = vk::StructureType::eBufferMemoryBarrier;
barrier.srcAccessMask = src_access;
barrier.dstAccessMask = dst_access;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.buffer = buffer;
barrier.size = VK_WHOLE_SIZE;
command.pipelineBarrier(src_stage, dst_stage, {}, 0, nullptr, 1, &barrier, 0, nullptr);
}
class Tiler final {
public:
void Run(bool to_tiled, GraphicContext* context, const void* input, void* output,
uint64_t tiled_capacity, uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
const GpuTileRecord& record);
void Release(GraphicContext* context);
private:
void Prepare(bool to_tiled, GraphicContext* context, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
std::vector<Dispatch>* dispatches) const;
void Init(GraphicContext* context);
void CreatePipelines(std::span<const Dispatch> dispatches);
void CreatePipeline(uint32_t pipeline_slot);
void Resize(uint64_t staging_size, uint64_t linear_size);
void CreateBuffer(uint64_t size, bool mapped, VulkanBuffer* buffer, void** data) const;
void Execute(bool to_tiled, const void* input, void* output, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const Dispatch> dispatches,
const GpuTileRecord& record);
void Destroy(Resources* target) const;
Common::Mutex mutex;
GraphicContext* ctx = nullptr;
Resources resources;
};
Tiler g_tiler;
void Tiler::Prepare(bool to_tiled, GraphicContext* context, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
std::vector<Dispatch>* dispatches) const {
EXIT_IF(context == nullptr || g_render_ctx == nullptr ||
g_render_ctx->GetGraphicCtx() != context || infos.empty() || tiled_capacity == 0 ||
linear_capacity == 0);
const auto& limits = context->GetPhysicalDeviceProperties().limits;
EXIT_NOT_IMPLEMENTED(tiled_capacity > UINT32_MAX || linear_capacity > UINT32_MAX ||
AlignToDword(tiled_capacity) > limits.maxStorageBufferRange ||
AlignToDword(linear_capacity) > limits.maxStorageBufferRange);
dispatches->clear();
dispatches->reserve(infos.size());
for (const auto& info: infos) {
TileBlockLayout block {};
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;
EXIT_NOT_IMPLEMENTED(
!TileGetBlockLayout(info.family, info.bytes_per_element, &block) || info.width == 0 ||
info.height == 0 || info.depth == 0 || info.pitch < info.width ||
(!info.tail && (tiled_width < info.width || tiled_height < info.height)) ||
!IsRangeValid(info.linear_offset, info.linear_size, linear_capacity) ||
!IsRangeValid(info.tiled_offset, info.tiled_size, tiled_capacity) ||
(block.block_depth == 1 && info.depth != 1));
uint64_t elements = 0, pitch_bytes = 0;
EXIT_NOT_IMPLEMENTED(!CheckedMultiply(info.width, info.height, &elements) ||
!CheckedMultiply(elements, info.depth, &elements) ||
!CheckedMultiply(info.pitch, info.bytes_per_element, &pitch_bytes) ||
elements > UINT32_MAX || pitch_bytes > UINT32_MAX);
uint64_t slice_bytes = info.linear_slice_stride;
EXIT_NOT_IMPLEMENTED(slice_bytes == 0 &&
!CheckedMultiply(pitch_bytes, info.height, &slice_bytes));
uint64_t linear_used = 0, minimum_slice = 0;
EXIT_NOT_IMPLEMENTED(!CheckedMultiply(pitch_bytes, info.height, &minimum_slice) ||
(info.depth > 1 && slice_bytes < minimum_slice) ||
!CheckedAddProduct(&linear_used, info.depth - 1u, slice_bytes) ||
!CheckedAddProduct(&linear_used, info.height - 1u, pitch_bytes) ||
!CheckedAddProduct(&linear_used, info.width, info.bytes_per_element) ||
linear_used > info.linear_size || slice_bytes > UINT32_MAX);
const uint64_t columns =
(static_cast<uint64_t>(tiled_width) + block.block_width - 1u) / block.block_width;
const uint64_t rows =
(static_cast<uint64_t>(tiled_height) + block.block_height - 1u) / block.block_height;
uint64_t blocks_per_slice = 0;
EXIT_NOT_IMPLEMENTED(!CheckedMultiply(columns, rows, &blocks_per_slice) ||
columns > UINT32_MAX || blocks_per_slice > UINT32_MAX ||
rows * block.block_height > UINT32_MAX);
if (info.tail) {
const bool supported = info.family != TileBlockFamily::Standard256B;
EXIT_NOT_IMPLEMENTED(
!supported || info.depth > block.block_depth || info.tail_x >= block.block_width ||
info.width > block.block_width - info.tail_x || info.tail_y >= block.block_height ||
info.height > block.block_height - info.tail_y ||
info.tiled_size < block.block_size);
} else {
const uint64_t slices =
(static_cast<uint64_t>(info.depth) + block.block_depth - 1u) / block.block_depth;
uint64_t tiled_used = 0;
EXIT_NOT_IMPLEMENTED(!CheckedMultiply(blocks_per_slice, slices, &tiled_used) ||
!CheckedMultiply(tiled_used, block.block_size, &tiled_used) ||
tiled_used > info.tiled_size);
}
const uint32_t alignment = std::min(info.bytes_per_element, 4u);
EXIT_NOT_IMPLEMENTED(((info.linear_offset | info.tiled_offset | pitch_bytes | slice_bytes) &
(alignment - 1u)) != 0);
Dispatch dispatch {};
dispatch.elements = static_cast<uint32_t>(elements);
dispatch.pipeline_slot = GetPipelineSlot(to_tiled, info.family, info.bytes_per_element);
dispatch.push.src_base =
static_cast<uint32_t>(to_tiled ? info.linear_offset : info.tiled_offset);
dispatch.push.dst_base =
static_cast<uint32_t>(to_tiled ? info.tiled_offset : info.linear_offset);
dispatch.push.width = info.width;
dispatch.push.height = info.height;
dispatch.push.depth = info.depth;
dispatch.push.surface_z = info.surface_z;
dispatch.push.pitch_bytes = static_cast<uint32_t>(pitch_bytes);
dispatch.push.slice_bytes = static_cast<uint32_t>(slice_bytes);
dispatch.push.blocks_per_row = static_cast<uint32_t>(columns);
dispatch.push.blocks_per_slice = static_cast<uint32_t>(blocks_per_slice);
dispatch.push.tail_x = info.tail_x;
dispatch.push.tail_y = info.tail_y;
dispatch.push.tail = info.tail;
dispatches->push_back(dispatch);
}
}
void Tiler::Destroy(Resources* target) const {
if (ctx == nullptr) {
return;
}
if (target->mapped != nullptr) {
VulkanUnmapMemory(ctx, &target->staging.memory);
}
if (target->staging.buffer != nullptr) {
VulkanDeleteBuffer(ctx, &target->staging);
}
if (target->linear.buffer != nullptr) {
VulkanDeleteBuffer(ctx, &target->linear);
}
for (auto pipeline: target->pipelines) {
if (pipeline != nullptr) {
ctx->device.destroyPipeline(pipeline, nullptr);
}
}
if (target->descriptor_pool != nullptr) {
ctx->device.destroyDescriptorPool(target->descriptor_pool, nullptr);
}
if (target->pipeline_layout != nullptr) {
ctx->device.destroyPipelineLayout(target->pipeline_layout, nullptr);
}
if (target->descriptor_layout != nullptr) {
ctx->device.destroyDescriptorSetLayout(target->descriptor_layout, nullptr);
}
*target = {};
}
void Tiler::Init(GraphicContext* context) {
if (resources.pipeline_layout != nullptr) {
EXIT_IF(ctx != context);
return;
}
EXIT_IF(context == nullptr || context->device == nullptr || context->allocator == nullptr);
ctx = context;
std::array<vk::DescriptorSetLayoutBinding, 2> bindings {};
for (uint32_t i = 0; i < bindings.size(); i++) {
bindings[i] = {i, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute,
nullptr};
}
vk::DescriptorSetLayoutCreateInfo descriptor_info {};
descriptor_info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
descriptor_info.bindingCount = static_cast<uint32_t>(bindings.size());
descriptor_info.pBindings = bindings.data();
RequireVulkanSuccess(ctx->device.createDescriptorSetLayout(&descriptor_info, nullptr,
&resources.descriptor_layout),
"create GPU tiler descriptor layout");
vk::PushConstantRange push_range {vk::ShaderStageFlagBits::eCompute, 0, sizeof(Push)};
vk::PipelineLayoutCreateInfo layout_info {};
layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &resources.descriptor_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = &push_range;
RequireVulkanSuccess(
ctx->device.createPipelineLayout(&layout_info, nullptr, &resources.pipeline_layout),
"create GPU tiler pipeline layout");
vk::DescriptorPoolSize pool_size {vk::DescriptorType::eStorageBuffer, 2};
vk::DescriptorPoolCreateInfo pool_info {};
pool_info.sType = vk::StructureType::eDescriptorPoolCreateInfo;
pool_info.maxSets = 1;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
RequireVulkanSuccess(
ctx->device.createDescriptorPool(&pool_info, nullptr, &resources.descriptor_pool),
"create GPU tiler descriptor pool");
vk::DescriptorSetAllocateInfo set_info {};
set_info.sType = vk::StructureType::eDescriptorSetAllocateInfo;
set_info.descriptorPool = resources.descriptor_pool;
set_info.descriptorSetCount = 1;
set_info.pSetLayouts = &resources.descriptor_layout;
RequireVulkanSuccess(ctx->device.allocateDescriptorSets(&set_info, &resources.descriptor_set),
"allocate GPU tiler descriptor set");
}
void Tiler::CreatePipeline(uint32_t pipeline_slot) {
const uint32_t element_size_index = pipeline_slot % BYTES_PER_ELEMENT_COUNT;
const uint32_t family_direction_index = pipeline_slot / BYTES_PER_ELEMENT_COUNT;
const uint32_t family_index = family_direction_index % FAMILY_COUNT;
const uint32_t direction_index = family_direction_index / FAMILY_COUNT;
const uint32_t specialization_values[] {1u << element_size_index, direction_index};
const vk::SpecializationMapEntry entries[] {{0, 0, 4}, {1, 4, 4}};
vk::SpecializationInfo specialization {2, entries, sizeof(specialization_values),
specialization_values};
vk::ShaderModuleCreateInfo module_info {};
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
module_info.codeSize = SHADERS[family_index].words * sizeof(uint32_t);
module_info.pCode = SHADERS[family_index].code;
vk::ShaderModule module = nullptr;
RequireVulkanSuccess(ctx->device.createShaderModule(&module_info, nullptr, &module),
"create GPU tiler shader module");
vk::PipelineShaderStageCreateInfo stage {};
stage.sType = vk::StructureType::ePipelineShaderStageCreateInfo;
stage.stage = vk::ShaderStageFlagBits::eCompute;
stage.module = module;
stage.pName = "main";
stage.pSpecializationInfo = &specialization;
vk::ComputePipelineCreateInfo info {};
info.sType = vk::StructureType::eComputePipelineCreateInfo;
info.stage = stage;
info.layout = resources.pipeline_layout;
vk::Pipeline pipeline = nullptr;
const auto result = ctx->device.createComputePipelines(nullptr, 1, &info, nullptr, &pipeline);
ctx->device.destroyShaderModule(module, nullptr);
RequireVulkanSuccess(result, "create GPU tiler pipeline");
resources.pipelines[pipeline_slot] = pipeline;
}
void Tiler::CreatePipelines(std::span<const Dispatch> dispatches) {
for (const auto& dispatch: dispatches) {
if (resources.pipelines[dispatch.pipeline_slot] == nullptr) {
CreatePipeline(dispatch.pipeline_slot);
}
}
}
void Tiler::CreateBuffer(uint64_t size, bool mapped, VulkanBuffer* buffer, void** data) const {
buffer->usage = vk::BufferUsageFlagBits::eStorageBuffer |
vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
buffer->memory.property =
mapped
? vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
: vk::MemoryPropertyFlags(vk::MemoryPropertyFlagBits::eDeviceLocal);
VulkanCreateBuffer(ctx, size, buffer);
if (mapped) VulkanMapMemory(ctx, &buffer->memory, data);
}
void Tiler::Resize(uint64_t staging_size, uint64_t linear_size) {
if (resources.staging.buffer_size >= staging_size &&
resources.linear.buffer_size >= linear_size) {
return;
}
staging_size = std::max(staging_size, resources.staging.buffer_size);
linear_size = std::max(linear_size, resources.linear.buffer_size);
VulkanBuffer staging {}, linear {};
void* mapped = nullptr;
CreateBuffer(staging_size, true, &staging, &mapped);
CreateBuffer(linear_size, false, &linear, nullptr);
if (resources.mapped != nullptr) VulkanUnmapMemory(ctx, &resources.staging.memory);
if (resources.staging.buffer != nullptr) VulkanDeleteBuffer(ctx, &resources.staging);
if (resources.linear.buffer != nullptr) VulkanDeleteBuffer(ctx, &resources.linear);
resources.staging = staging;
resources.linear = linear;
resources.mapped = mapped;
}
void Tiler::Execute(bool to_tiled, const void* input, void* output, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const Dispatch> dispatches,
const GpuTileRecord& record) {
const uint64_t tiled_size = AlignToDword(tiled_capacity);
const uint64_t linear_size = AlignToDword(linear_capacity);
const uint64_t input_size = to_tiled ? linear_capacity : tiled_capacity;
if (input != nullptr) {
std::memcpy(resources.mapped, input, static_cast<size_t>(input_size));
std::memset(static_cast<uint8_t*>(resources.mapped) + input_size, 0,
static_cast<size_t>(AlignToDword(input_size) - input_size));
}
std::array<vk::DescriptorBufferInfo, 2> buffer_info {{
{to_tiled ? resources.linear.buffer : resources.staging.buffer, 0,
to_tiled ? linear_size : tiled_size},
{to_tiled ? resources.staging.buffer : resources.linear.buffer, 0,
to_tiled ? tiled_size : linear_size},
}};
std::array<vk::WriteDescriptorSet, 2> writes {};
for (uint32_t i = 0; i < writes.size(); i++) {
writes[i].sType = vk::StructureType::eWriteDescriptorSet;
writes[i].dstSet = resources.descriptor_set;
writes[i].dstBinding = i;
writes[i].descriptorCount = 1;
writes[i].descriptorType = vk::DescriptorType::eStorageBuffer;
writes[i].pBufferInfo = &buffer_info[i];
}
ctx->device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0,
nullptr);
CommandBuffer command(GraphicContext::QUEUE_UTIL);
command.Begin();
auto vk_command = command.Handle();
if (input != nullptr) {
Barrier(vk_command, resources.staging.buffer, vk::AccessFlagBits::eHostWrite,
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead,
vk::PipelineStageFlagBits::eHost,
vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eTransfer);
}
if (to_tiled && input != nullptr) {
const vk::BufferCopy copy {0, 0, linear_size};
vk_command.copyBuffer(resources.staging.buffer, resources.linear.buffer, 1, &copy);
Barrier(vk_command, resources.linear.buffer, vk::AccessFlagBits::eTransferWrite,
vk::AccessFlagBits::eShaderRead, vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eComputeShader);
Barrier(vk_command, resources.staging.buffer, vk::AccessFlagBits::eTransferRead,
vk::AccessFlagBits::eTransferWrite, vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer);
}
if (to_tiled && record) {
record(&command, &resources.linear);
Barrier(vk_command, resources.linear.buffer,
vk::AccessFlagBits::eTransferWrite | vk::AccessFlagBits::eMemoryWrite,
vk::AccessFlagBits::eShaderRead, vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eComputeShader);
}
const auto output_buffer = to_tiled ? resources.staging.buffer : resources.linear.buffer;
const auto output_size = to_tiled ? tiled_size : linear_size;
vk_command.fillBuffer(output_buffer, 0, output_size, 0);
Barrier(vk_command, output_buffer, vk::AccessFlagBits::eTransferWrite,
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eComputeShader);
vk_command.bindDescriptorSets(vk::PipelineBindPoint::eCompute, resources.pipeline_layout, 0, 1,
&resources.descriptor_set, 0, nullptr);
const uint64_t limit =
static_cast<uint64_t>(
ctx->GetPhysicalDeviceProperties().limits.maxComputeWorkGroupCount[0]) *
GROUP_SIZE;
for (const auto& dispatch: dispatches) {
vk_command.bindPipeline(vk::PipelineBindPoint::eCompute,
resources.pipelines[dispatch.pipeline_slot]);
for (uint32_t first = 0; first < dispatch.elements;) {
auto push = dispatch.push;
push.first = first;
push.count =
static_cast<uint32_t>(std::min<uint64_t>(dispatch.elements - first, limit));
vk_command.pushConstants(resources.pipeline_layout, vk::ShaderStageFlagBits::eCompute,
0, sizeof(push), &push);
vk_command.dispatch((push.count - 1u) / GROUP_SIZE + 1u, 1, 1);
first += push.count;
}
}
Barrier(vk_command, output_buffer, vk::AccessFlagBits::eShaderWrite,
vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eHostRead,
vk::PipelineStageFlagBits::eComputeShader,
vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eHost);
if (!to_tiled && record) {
record(&command, &resources.linear);
}
if (!to_tiled && output != nullptr) {
const vk::BufferCopy copy {0, 0, linear_size};
vk_command.copyBuffer(resources.linear.buffer, resources.staging.buffer, 1, &copy);
Barrier(vk_command, resources.staging.buffer, vk::AccessFlagBits::eTransferWrite,
vk::AccessFlagBits::eHostRead, vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eHost);
}
command.End();
command.Execute();
command.WaitForFence();
if (output != nullptr) {
std::memcpy(output, resources.mapped,
static_cast<size_t>(to_tiled ? tiled_capacity : linear_capacity));
}
}
void Tiler::Run(bool to_tiled, GraphicContext* context, const void* input, void* output,
uint64_t tiled_capacity, uint64_t linear_capacity,
std::span<const GpuTileInfo> infos, const GpuTileRecord& record) {
Common::LockGuard lock(mutex);
EXIT_IF((to_tiled && (output == nullptr || (input == nullptr && !record))) ||
(!to_tiled && (input == nullptr || (output == nullptr && !record))));
std::vector<Dispatch> dispatches;
Prepare(to_tiled, context, tiled_capacity, linear_capacity, infos, &dispatches);
Init(context);
CreatePipelines(dispatches);
const uint64_t staging_size =
std::max(AlignToDword(tiled_capacity), AlignToDword(linear_capacity));
const uint64_t linear_size = AlignToDword(linear_capacity);
Resize(staging_size, linear_size);
Execute(to_tiled, input, output, tiled_capacity, linear_capacity, dispatches, record);
}
void Tiler::Release(GraphicContext* context) {
Common::LockGuard lock(mutex);
EXIT_IF(ctx != nullptr && context != ctx);
Destroy(&resources);
ctx = nullptr;
}
} // namespace
void GpuDetile(GraphicContext* ctx, const void* tiled, void* linear, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
const GpuTileRecord& after) {
g_tiler.Run(false, ctx, tiled, linear, tiled_capacity, linear_capacity, infos, after);
}
void GpuTile(GraphicContext* ctx, const void* linear, void* tiled, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
const GpuTileRecord& before) {
g_tiler.Run(true, ctx, linear, tiled, tiled_capacity, linear_capacity, infos, before);
}
void GpuTileRelease(GraphicContext* ctx) {
g_tiler.Release(ctx);
}
} // namespace Libs::Graphics
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include "graphics/guest_gpu/tile.h"
#include <cstdint>
#include <functional>
#include <span>
namespace Libs::Graphics {
struct GraphicContext;
struct VulkanBuffer;
class CommandBuffer;
struct GpuTileInfo {
TileBlockFamily family = TileBlockFamily::Count;
uint32_t bytes_per_element = 0;
uint64_t linear_offset = 0;
uint64_t linear_size = 0;
uint64_t tiled_offset = 0;
uint64_t tiled_size = 0;
uint64_t linear_slice_stride = 0;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 1;
uint32_t pitch = 0;
uint32_t tail_x = 0;
uint32_t tail_y = 0;
bool tail = false;
uint32_t tiled_width = 0;
uint32_t tiled_height = 0;
uint32_t surface_z = 0;
};
using GpuTileRecord = std::function<void(CommandBuffer*, VulkanBuffer*)>;
void GpuDetile(GraphicContext* ctx, const void* tiled, void* linear, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
const GpuTileRecord& after = {});
void GpuTile(GraphicContext* ctx, const void* linear, void* tiled, uint64_t tiled_capacity,
uint64_t linear_capacity, std::span<const GpuTileInfo> infos,
const GpuTileRecord& before = {});
void GpuTileRelease(GraphicContext* ctx);
} // namespace Libs::Graphics
+302 -392
View File
@@ -11,6 +11,7 @@
#include <algorithm>
#include <atomic>
#include <bit>
#include <cinttypes>
#include <cstring>
@@ -165,17 +166,26 @@ static uint64_t GetLevelSrcSize(const TileSizeOffset& level_size) {
return (level_size.src_size != 0 ? level_size.src_size : level_size.size);
}
static uint32_t GetTextureLevelDepth(uint32_t depth, uint32_t level, bool volume_texture) {
return volume_texture ? std::max(depth >> level, 1u) : depth;
}
static size_t GetTextureRegionCount(uint32_t depth, uint64_t levels, bool volume_texture) {
size_t count = 0;
for (uint32_t level = 0; level < levels; level++) {
count += GetTextureLevelDepth(depth, level, volume_texture);
}
return count;
}
uint64_t TextureUploadSliceSourceOffset(const TextureUploadLayout& layout, uint32_t level,
uint32_t slice,
TextureUploadSliceLayout source_slice_layout) {
uint32_t slice) {
if (level >= 16 || layout.level_sizes[level].size == 0) {
EXIT("invalid texture upload slice source, level=%u slice=%u\n", level, slice);
}
const auto level_offset = GetLevelSrcOffset(layout.level_sizes[level]);
const auto slice_stride =
source_slice_layout == TextureUploadSliceLayout::MipChainPerSlice
? (layout.source_slice_stride != 0 ? layout.source_slice_stride : layout.slice_stride)
: GetLevelSrcSize(layout.level_sizes[level]);
layout.source_slice_stride != 0 ? layout.source_slice_stride : layout.slice_stride;
if (slice_stride != 0 && slice > (UINT64_MAX - level_offset) / slice_stride) {
EXIT("texture upload slice source offset overflow, level=%u slice=%u\n", level, slice);
}
@@ -184,7 +194,7 @@ uint64_t TextureUploadSliceSourceOffset(const TextureUploadLayout& layout, uint3
uint64_t TextureCalcUploadSize(const TextureUploadLayout& layout,
const std::vector<BufferImageCopy>& regions, uint64_t levels,
uint32_t depth, TextureUploadSliceLayout source_slice_layout) {
uint32_t depth) {
uint64_t size = 0;
for (const auto& r: regions) {
@@ -193,61 +203,17 @@ uint64_t TextureCalcUploadSize(const TextureUploadLayout& layout,
}
for (uint32_t level = 0; level < levels; level++) {
const auto src_size = GetLevelSrcSize(layout.level_sizes[level]);
for (uint32_t z = 0; z < depth; z++) {
size = std::max<uint64_t>(
size,
TextureUploadSliceSourceOffset(layout, level, z, source_slice_layout) + src_size);
const auto src_size = GetLevelSrcSize(layout.level_sizes[level]);
const auto mip_depth = GetTextureLevelDepth(depth, level, layout.volume_texture);
for (uint32_t z = 0; z < mip_depth; z++) {
size = std::max<uint64_t>(size,
TextureUploadSliceSourceOffset(layout, level, z) + src_size);
}
}
return size;
}
void TextureCopyBufferBytes(GraphicContext* ctx, VulkanBuffer* src_buffer,
uint64_t src_buffer_offset, uint64_t copy_size,
Transfer::ScratchBuffer* dst) {
EXIT_IF(ctx == nullptr);
EXIT_IF(src_buffer == nullptr);
EXIT_IF(dst == nullptr);
EXIT_IF(dst->Data() == nullptr);
std::memset(dst->Data(), 0, copy_size);
if (copy_size == 0 || src_buffer_offset >= src_buffer->buffer_size) {
return;
}
const auto available =
std::min<uint64_t>(copy_size, src_buffer->buffer_size - src_buffer_offset);
if (available == 0) {
return;
}
if (src_buffer->memory.property & vk::MemoryPropertyFlagBits::eHostVisible) {
void* data = nullptr;
VulkanMapMemory(ctx, &src_buffer->memory, &data);
std::memcpy(dst->Data(), static_cast<const uint8_t*>(data) + src_buffer_offset, available);
VulkanUnmapMemory(ctx, &src_buffer->memory);
return;
}
VulkanBuffer readback {};
readback.usage = vk::BufferUsageFlagBits::eTransferDst;
readback.memory.property = vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent |
vk::MemoryPropertyFlagBits::eHostCached;
VulkanCreateBuffer(ctx, (src_buffer_offset + available + 3u) & ~uint64_t {3}, &readback);
Transfer::CopyBuffer(src_buffer, &readback, src_buffer_offset + available);
void* data = nullptr;
VulkanMapMemory(ctx, &readback.memory, &data);
std::memcpy(dst->Data(), static_cast<const uint8_t*>(data) + src_buffer_offset, available);
VulkanUnmapMemory(ctx, &readback.memory);
VulkanDeleteBuffer(ctx, &readback);
}
vk::ComponentSwizzle TextureGetComponentSwizzle(uint8_t s) {
switch (static_cast<Prospero::CompSwizzle>(s)) {
case Prospero::CompSwizzle::kZero: return vk::ComponentSwizzle::eZero;
@@ -377,77 +343,6 @@ static uint32_t AlignUpU32(uint32_t value, uint32_t alignment) {
return (value + alignment - 1u) & ~(alignment - 1u);
}
static uint32_t ShiftCeilU32(uint32_t value, uint32_t shift) {
return static_cast<uint32_t>((static_cast<uint64_t>(value) + (1ull << shift) - 1ull) >> shift);
}
struct Standard4KBVolumeMipLayout {
uint32_t first_tail_level = 0;
uint32_t block_depth = 1;
uint64_t block_slice_size = 0;
uint64_t level_offsets[16] = {};
uint64_t level_sizes[16] = {};
};
static bool CalcStandard4kbVolumeMipLayout(uint32_t format, uint32_t pitch, uint32_t height,
uint64_t levels, Standard4KBVolumeMipLayout* out) {
EXIT_IF(out == nullptr);
EXIT_NOT_IMPLEMENTED(levels == 0 || levels > 16);
uint32_t bytes_per_element = 0;
uint32_t texels_per_element_wide = 0;
uint32_t texels_per_element_tall = 0;
uint32_t block_width_log2 = 0;
uint32_t block_height_log2 = 0;
uint32_t block_depth_log2 = 0;
if (!TileGetStandard4KBVolumeLayout(format, &bytes_per_element, &texels_per_element_wide,
&texels_per_element_tall, &block_width_log2,
&block_height_log2, &block_depth_log2)) {
return false;
}
const uint32_t row_elements0 =
std::max((pitch + texels_per_element_wide - 1u) / texels_per_element_wide, 1u);
const uint32_t height_elements0 =
std::max((height + texels_per_element_tall - 1u) / texels_per_element_tall, 1u);
const uint32_t block_width = 1u << block_width_log2;
const uint32_t block_height = 1u << block_height_log2;
const uint32_t block_depth = 1u << block_depth_log2;
const uint32_t tail_width_limit = block_width;
const uint32_t tail_height_limit = block_height >> 1u;
constexpr uint32_t max_tail_levels = 5u;
out->first_tail_level = static_cast<uint32_t>(levels);
out->block_depth = block_depth;
out->block_slice_size = 0;
for (uint32_t level = 0; level < levels; level++) {
const uint32_t row_elements = std::max(ShiftCeilU32(row_elements0, level), 1u);
const uint32_t height_elements = std::max(ShiftCeilU32(height_elements0, level), 1u);
out->level_offsets[level] = out->block_slice_size;
if (row_elements <= tail_width_limit && height_elements <= tail_height_limit &&
levels - level <= max_tail_levels) {
out->first_tail_level = level;
out->level_sizes[level] = 4096u;
out->block_slice_size += 4096u;
for (uint32_t tail_level = level + 1; tail_level < levels; tail_level++) {
out->level_offsets[tail_level] = out->level_offsets[level];
out->level_sizes[tail_level] = 4096u;
}
break;
}
out->level_sizes[level] = static_cast<uint64_t>(block_depth) *
AlignUpU32(row_elements, block_width) *
AlignUpU32(height_elements, block_height) * bytes_per_element;
out->block_slice_size += out->level_sizes[level];
}
return out->block_slice_size != 0;
}
uint32_t TextureGetAtlasSliceYStride(vk::Format format, uint32_t mip_height, uint32_t depth,
uint64_t levels) {
return (depth > 1 && levels > 1 && Transfer::IsBlockCompressedFormat(format)
@@ -733,9 +628,8 @@ static uint64_t CalcLinearUploadLevelSize(uint32_t fmt, uint32_t pitch, uint32_t
return 0;
}
static uint64_t FillVolumeLinearUploadLevels(TileSizeOffset* level_sizes, uint32_t fmt,
uint64_t height, uint64_t levels,
uint32_t base_pitch) {
static uint64_t SetLinearUploadLevels(TileSizeOffset* level_sizes, uint32_t fmt, uint64_t height,
uint64_t levels, uint32_t base_pitch) {
uint64_t offset = 0;
auto pitch = base_pitch;
auto h = static_cast<uint32_t>(height);
@@ -767,9 +661,8 @@ static uint64_t FillVolumeLinearUploadLevels(TileSizeOffset* level_sizes, uint32
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64_t height,
uint64_t levels, uint32_t depth, uint64_t pitch,
uint64_t tile, uint64_t upload_size,
bool allow_depth_tile,
bool require_single_mip_small_tiles,
bool volume_texture, const char* owner) {
bool allow_depth_tile, bool volume_texture,
const char* owner) {
TextureUploadLayout layout {};
layout.tile = static_cast<uint32_t>(tile);
layout.pitch = static_cast<uint32_t>(pitch);
@@ -778,33 +671,45 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64
if (fmt != 0) {
if (layout.tile != 0) {
const auto tile_mode = static_cast<Prospero::TileMode>(layout.tile);
layout.fmt_tiled_render_target =
(Prospero::RenderTargetBytesPerElement(static_cast<uint32_t>(fmt)) != 0 &&
tile_mode == Prospero::TileMode::kRenderTarget);
layout.fmt_tiled_standard256b =
(TileIsStandard256BTextureSupported(static_cast<uint32_t>(fmt)) &&
tile_mode == Prospero::TileMode::kStandard256B &&
(!require_single_mip_small_tiles || levels == 1));
layout.fmt_tiled_standard4kb =
(TileIsStandard4KBTextureSupported(static_cast<uint32_t>(fmt)) &&
tile_mode == Prospero::TileMode::kStandard4KB &&
(!require_single_mip_small_tiles || levels == 1));
layout.fmt_tiled_standard64kb =
(TileIsStandard64KBTextureSupported(static_cast<uint32_t>(fmt)) &&
tile_mode == Prospero::TileMode::kStandard64KB);
layout.fmt_tiled_depth =
(allow_depth_tile &&
Prospero::RenderTargetBytesPerElement(static_cast<uint32_t>(fmt)) != 0 &&
tile_mode == Prospero::TileMode::kDepth);
if (!layout.fmt_tiled_render_target && !layout.fmt_tiled_standard256b &&
!layout.fmt_tiled_standard4kb && !layout.fmt_tiled_standard64kb &&
!layout.fmt_tiled_depth) {
EXIT("%s: unsupported typed tiled upload, using linear fallback: fmt=%u tile=%u "
switch (tile_mode) {
case Prospero::TileMode::kStandard256B:
if (TileIsStandard256BTextureSupported(fmt)) {
layout.tile_family = TileBlockFamily::Standard256B;
}
break;
case Prospero::TileMode::kStandard4KB:
if (TileIsStandard4KBTextureSupported(fmt)) {
layout.tile_family = TileBlockFamily::Standard4KB;
}
break;
case Prospero::TileMode::kStandard64KB:
if (TileIsStandard64KBTextureSupported(fmt)) {
layout.tile_family = TileBlockFamily::Standard64KB;
}
break;
case Prospero::TileMode::kPrt:
if (TileIsStandard64KBTextureSupported(fmt)) {
layout.tile_family = TileBlockFamily::Prt64KB;
}
break;
case Prospero::TileMode::kRenderTarget:
if (Prospero::RenderTargetBytesPerElement(fmt) != 0) {
layout.tile_family = TileBlockFamily::RenderTarget64KB;
}
break;
case Prospero::TileMode::kDepth:
if (allow_depth_tile && Prospero::RenderTargetBytesPerElement(fmt) != 0) {
layout.tile_family = TileBlockFamily::Depth64KB;
}
break;
default: break;
}
if (layout.tile_family == TileBlockFamily::Count) {
EXIT("%s: unsupported typed tiled upload: fmt=%u tile=%u "
"size=%" PRIu64 " extent=%" PRIu64 "x%" PRIu64 " pitch=%" PRIu64
" levels=%" PRIu64 "\n",
owner, static_cast<uint32_t>(fmt), layout.tile, upload_size, width, height,
pitch, levels);
layout.tile = 0;
}
}
@@ -813,28 +718,29 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64
TileGetTextureSize(fmt, width, height, layout.pitch, levels, layout.tile, nullptr,
layout.level_sizes, layout.padded_sizes);
if (layout.volume_texture) {
TileSizeOffset source_levels[16] {};
std::copy_n(layout.level_sizes, levels, source_levels);
layout.slice_stride =
FillVolumeLinearUploadLevels(layout.level_sizes, fmt, height, levels, layout.pitch);
if (layout.fmt_tiled_render_target) {
if (static_cast<Prospero::TileMode>(layout.tile) != Prospero::TileMode::kLinear) {
if (layout.volume_texture) {
layout.slice_stride = SetLinearUploadLevels(layout.level_sizes, fmt, height, levels,
static_cast<uint32_t>(width));
} else {
TileSizeOffset tiled_levels[16] {};
std::copy_n(layout.level_sizes, levels, tiled_levels);
layout.source_slice_stride =
CalcTextureSliceStride(source_levels, levels, upload_size, depth);
for (uint32_t level = 0; level < levels; level++) {
layout.level_sizes[level].src_offset = source_levels[level].offset;
layout.level_sizes[level].src_size = source_levels[level].size;
layout.level_sizes[level].x = source_levels[level].x;
layout.level_sizes[level].y = source_levels[level].y;
CalcTextureSliceStride(tiled_levels, levels, upload_size, depth);
SetLinearUploadLevels(layout.level_sizes, fmt, height, levels, layout.pitch);
for (uint32_t i = 0; i < levels; ++i) {
if (tiled_levels[i].src_size > tiled_levels[i].size) {
layout.first_tail_level = std::min(layout.first_tail_level, i);
}
layout.level_sizes[i].src_offset = GetLevelSrcOffset(tiled_levels[i]);
layout.level_sizes[i].src_size = GetLevelSrcSize(tiled_levels[i]);
layout.level_sizes[i].x = tiled_levels[i].x;
layout.level_sizes[i].y = tiled_levels[i].y;
}
}
}
if (layout.fmt_tiled_depth) {
for (uint32_t i = 0; i < levels; i++) {
layout.level_sizes[i].x = layout.padded_sizes[i].width;
layout.level_sizes[i].y = layout.padded_sizes[i].height;
}
} else if (layout.volume_texture) {
layout.slice_stride =
CalcTextureSliceStride(layout.level_sizes, levels, upload_size, depth);
}
} else {
EXIT("%s: legacy texture upload format unsupported: fmt=0 tile=%u size=%" PRIu64
@@ -849,66 +755,68 @@ TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64
return layout;
}
std::vector<BufferImageCopy> TextureBuildUploadRegions(
const TextureUploadLayout& layout, vk::Format image_format, uint32_t width, uint32_t height,
uint32_t depth, uint64_t levels, bool array_texture, bool volume_texture,
TextureUploadDestination destination, TextureUploadSliceLayout slice_layout) {
std::vector<BufferImageCopy> TextureBuildUploadRegions(const TextureUploadLayout& layout,
vk::Format image_format, uint32_t width,
uint32_t height, uint32_t depth,
uint64_t levels, bool array_texture,
bool volume_texture,
TextureUploadDestination destination) {
uint32_t mip_width = width;
uint32_t mip_height = height;
uint32_t mip_pitch = layout.pitch;
uint32_t mip_pitch = volume_texture && static_cast<Prospero::TileMode>(layout.tile) !=
Prospero::TileMode::kLinear
? width
: layout.pitch;
std::vector<BufferImageCopy> regions(levels * depth);
std::vector<BufferImageCopy> regions;
regions.reserve(GetTextureRegionCount(depth, levels, volume_texture));
for (uint32_t i = 0; i < levels; i++) {
EXIT_NOT_IMPLEMENTED(layout.level_sizes[i].size == 0);
const auto mipmap_offset = Transfer::MipmapAtlasOffset(i, width, height);
const auto mip_depth = GetTextureLevelDepth(depth, i, volume_texture);
for (uint32_t z = 0; z < depth; z++) {
const auto region_index = i * depth + z;
const auto slice_offset = (slice_layout == TextureUploadSliceLayout::MipChainPerSlice
? z * layout.slice_stride
: z * static_cast<uint64_t>(layout.level_sizes[i].size));
for (uint32_t z = 0; z < mip_depth; z++) {
const auto slice_offset = z * layout.slice_stride;
BufferImageCopy region {};
regions[region_index].offset =
static_cast<uint32_t>(layout.level_sizes[i].offset + slice_offset);
regions[region_index].width = mip_width;
regions[region_index].height = mip_height;
regions[region_index].copy_height =
region.offset = static_cast<uint32_t>(layout.level_sizes[i].offset + slice_offset);
region.width = mip_width;
region.height = mip_height;
region.copy_height =
(!array_texture && !volume_texture && depth > 1 && levels > 1 &&
Transfer::IsBlockCompressedFormat(image_format)
? TextureGetAtlasSliceYStride(image_format, mip_height, depth, levels)
: 0);
regions[region_index].dst_layer = (array_texture ? z : 0);
regions[region_index].dst_z = (volume_texture ? static_cast<int>(z) : 0);
if (layout.fmt_tiled_depth && layout.level_sizes[i].x != 0) {
regions[region_index].pitch = layout.level_sizes[i].x;
} else if (!layout.volume_texture &&
static_cast<Prospero::TileMode>(layout.tile) ==
Prospero::TileMode::kLinear &&
layout.padded_sizes[i].width != 0) {
regions[region_index].pitch = layout.padded_sizes[i].width;
region.dst_layer = (array_texture ? z : 0);
region.dst_z = (volume_texture ? static_cast<int>(z) : 0);
if (!layout.volume_texture &&
static_cast<Prospero::TileMode>(layout.tile) == Prospero::TileMode::kLinear &&
layout.padded_sizes[i].width != 0) {
region.pitch = layout.padded_sizes[i].width;
} else {
regions[region_index].pitch = mip_pitch;
region.pitch = mip_pitch;
}
if (destination == TextureUploadDestination::MipLevels) {
regions[region_index].dst_level = i;
regions[region_index].dst_x = 0;
regions[region_index].dst_y =
region.dst_level = i;
region.dst_x = 0;
region.dst_y =
(array_texture || volume_texture
? 0
: static_cast<int>(z * TextureGetAtlasSliceYStride(
image_format, mip_height, depth, levels)));
} else {
regions[region_index].dst_level = 0;
regions[region_index].dst_x = mipmap_offset.first;
regions[region_index].dst_y =
region.dst_level = 0;
region.dst_x = mipmap_offset.first;
region.dst_y =
(array_texture || volume_texture
? mipmap_offset.second
: mipmap_offset.second +
static_cast<int>(z * TextureGetAtlasSliceYStride(
image_format, mip_height, depth, levels)));
}
regions.push_back(region);
}
if (mip_width > 1) {
@@ -925,12 +833,16 @@ std::vector<BufferImageCopy> TextureBuildUploadRegions(
return regions;
}
static uint64_t GetSliceSrcStride(const TextureUploadLayout& layout, uint32_t level,
TextureUploadSliceLayout source_slice_layout) {
return (
source_slice_layout == TextureUploadSliceLayout::MipChainPerSlice
? (layout.source_slice_stride != 0 ? layout.source_slice_stride : layout.slice_stride)
: GetLevelSrcSize(layout.level_sizes[level]));
std::vector<ImageBufferCopy>
TextureBuildDownloadRegions(const std::vector<BufferImageCopy>& upload_regions) {
std::vector<ImageBufferCopy> regions;
regions.reserve(upload_regions.size());
for (const auto& region: upload_regions) {
regions.push_back({region.offset, region.pitch, region.dst_level, region.width,
region.height, region.copy_height, region.dst_layer, region.dst_x,
region.dst_y, region.dst_z, region.aspect});
}
return regions;
}
static uint64_t FmaskRegionCopySize(const BufferImageCopy& region) {
@@ -969,193 +881,191 @@ static void UploadFmaskIdentity(GraphicContext* ctx, VulkanImage* vk_obj,
Transfer::UploadImage(ctx, vk_obj, temp_buf.Data(), upload_size, upload_regions, dst_layout);
}
struct GpuTileElementLayout {
uint32_t bytes = 0;
uint32_t wide = 1;
uint32_t tall = 1;
};
static bool GetGpuTileElementLayout(uint32_t fmt, GpuTileElementLayout* out) {
EXIT_IF(out == nullptr);
if (const auto bytes = Prospero::NumBytesPerElement(fmt); bytes != 0) {
*out = {bytes, 1, 1};
return true;
}
if (const auto bytes = Prospero::BlockCompressedBytesPerBlock(fmt); bytes != 0) {
*out = {bytes, 4, 4};
return true;
}
return false;
}
static bool SetGpuTileSize(uint64_t offset, uint64_t length, uint64_t capacity, uint64_t* size) {
if (size == nullptr || offset > capacity || length > capacity - offset) {
return false;
}
*size = length;
return true;
}
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<BufferImageCopy>& regions,
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
uint64_t levels, std::vector<GpuTileInfo>* out_infos) {
if (out_infos == nullptr || size == 0 || levels == 0 || levels > 16 || depth == 0 ||
regions.size() != GetTextureRegionCount(depth, levels, layout.volume_texture) ||
Prospero::IsFmaskTextureFormat(fmt)) {
return false;
}
GpuTileElementLayout element {};
if (layout.tile_family == TileBlockFamily::RenderTarget64KB ||
layout.tile_family == TileBlockFamily::Depth64KB) {
element.bytes = Prospero::RenderTargetBytesPerElement(fmt);
} else if (!GetGpuTileElementLayout(fmt, &element)) {
return false;
}
if (element.bytes == 0) {
return false;
}
std::vector<GpuTileInfo> infos;
infos.reserve(regions.size());
if (layout.volume_texture) {
TileVolumeLayout volume {};
if (!TileGetTextureVolumeLayout(fmt, regions[0].width, regions[0].height, depth,
static_cast<uint32_t>(levels), layout.tile, &volume)) {
return false;
}
element = {volume.bytes_per_element, volume.texel_width, volume.texel_height};
TileBlockLayout block {};
if (!TileGetBlockLayout(volume.family, element.bytes, &block)) return false;
size_t region_base = 0;
for (uint32_t level = 0; level < levels; ++level) {
const uint32_t mip_depth = GetTextureLevelDepth(depth, level, true);
const bool tail = level >= volume.first_tail_level;
const uint64_t linear_stride = layout.slice_stride;
for (uint32_t z = 0; z < mip_depth; z += block.block_depth) {
const uint32_t copy_depth = std::min(block.block_depth, mip_depth - z);
const auto& region = regions[region_base + z];
GpuTileInfo info {};
info.family = block.family;
info.bytes_per_element = block.bytes_per_element;
info.linear_offset = region.offset;
info.tiled_offset =
static_cast<uint64_t>(z / block.block_depth) * volume.block_slice_size +
volume.level_offsets[level];
const uint64_t linear_span =
static_cast<uint64_t>(copy_depth - 1u) * linear_stride +
layout.level_sizes[level].size;
if (!SetGpuTileSize(info.linear_offset, linear_span, size, &info.linear_size) ||
!SetGpuTileSize(info.tiled_offset, volume.level_sizes[level], size,
&info.tiled_size)) {
return false;
}
info.linear_slice_stride = linear_stride;
info.width = std::max((region.width + element.wide - 1u) / element.wide, 1u);
info.height = std::max((region.height + element.tall - 1u) / element.tall, 1u);
info.depth = copy_depth;
info.surface_z = block.block_depth == 1 ? static_cast<uint32_t>(region.dst_z) : 0;
info.pitch = std::max((region.pitch + element.wide - 1u) / element.wide, 1u);
info.tail_x = tail ? volume.tail_x[level] : 0;
info.tail_y = tail ? volume.tail_y[level] : 0;
info.tail = tail;
info.tiled_width = volume.level_widths[level];
info.tiled_height = volume.level_heights[level];
infos.push_back(info);
}
region_base += mip_depth;
}
} else {
const auto base_family = layout.tile_family;
if (base_family == TileBlockFamily::Count) {
return false;
}
size_t region_index = 0;
for (uint32_t level = 0; level < levels; level++) {
const auto& level_size = layout.level_sizes[level];
const bool tail = level >= layout.first_tail_level;
const auto family = base_family;
TileBlockLayout block {};
if (!TileGetBlockLayout(family, element.bytes, &block)) {
return false;
}
const auto level_depth = GetTextureLevelDepth(depth, level, layout.volume_texture);
for (uint32_t z = 0; z < level_depth; z++) {
const auto& region = regions[region_index++];
GpuTileInfo info {};
info.family = block.family;
info.bytes_per_element = block.bytes_per_element;
info.linear_offset = region.offset;
info.tiled_offset = TextureUploadSliceSourceOffset(layout, level, z);
if (!SetGpuTileSize(info.linear_offset, level_size.size, size, &info.linear_size) ||
!SetGpuTileSize(info.tiled_offset, GetLevelSrcSize(level_size), size,
&info.tiled_size)) {
return false;
}
info.width = std::max((region.width + element.wide - 1u) / element.wide, 1u);
info.height = std::max((region.height + element.tall - 1u) / element.tall, 1u);
info.surface_z = base_family == TileBlockFamily::RenderTarget64KB ||
base_family == TileBlockFamily::Depth64KB
? region.dst_layer
: 0;
info.pitch = std::max((region.pitch + element.wide - 1u) / element.wide, 1u);
info.tail = tail;
info.tail_x = tail ? level_size.x : 0;
info.tail_y = tail ? level_size.y : 0;
info.tiled_width =
layout.padded_sizes[level].width != 0
? std::max((layout.padded_sizes[level].width + element.wide - 1u) /
element.wide,
1u)
: info.pitch;
info.tiled_height =
layout.padded_sizes[level].height != 0
? std::max((layout.padded_sizes[level].height + element.tall - 1u) /
element.tall,
1u)
: info.height;
infos.push_back(info);
}
}
}
if (infos.empty()) {
return false;
}
*out_infos = std::move(infos);
return true;
}
void TextureUploadGuestImage(GraphicContext* ctx, VulkanImage* vk_obj, const void* src_data,
uint64_t size, const std::vector<BufferImageCopy>& regions,
const TextureUploadLayout& layout, uint32_t fmt, uint64_t width,
uint64_t height, uint32_t depth, uint64_t levels,
TextureUploadSliceLayout source_slice_layout, const char* owner,
uint64_t height, uint32_t depth, uint64_t levels, const char* owner,
vk::ImageLayout dst_layout) {
if (fmt == 0) {
EXIT("%s: legacy texture upload format unsupported: fmt=0 tile=%u size=%" PRIu64
" extent=%" PRIu64 "x%" PRIu64 " depth=%u pitch=%u levels=%" PRIu64 "\n",
EXIT("%s: texture upload format unsupported: fmt=0 tile=%u size=%" PRIu64 " extent=%" PRIu64
"x%" PRIu64 " depth=%u pitch=%u levels=%" PRIu64 "\n",
owner, layout.tile, size, width, height, depth, layout.pitch, levels);
} else if (static_cast<Prospero::TileMode>(layout.tile) == Prospero::TileMode::kLinear) {
Transfer::UploadImage(ctx, vk_obj, src_data, size, regions, dst_layout);
} else if (layout.fmt_tiled_render_target) {
LOGF("%s: detiling typed render-target texture: fmt=%u tile=%u size=%" PRIu64
" extent=%" PRIu64 "x%" PRIu64 " depth=%" PRIu64 " pitch=%u levels=%" PRIu64 "\n",
owner, static_cast<uint32_t>(fmt), layout.tile, size, width, height,
static_cast<uint64_t>(depth), layout.pitch, levels);
Transfer::ScratchBuffer temp_buf(size);
for (uint32_t i = 0; i < levels; i++) {
for (uint32_t z = 0; z < depth; z++) {
const auto region_index = i * depth + z;
auto* dst = static_cast<uint8_t*>(temp_buf.Data()) + regions[region_index].offset;
const auto* src = static_cast<const uint8_t*>(src_data) +
TextureUploadSliceSourceOffset(layout, i, z, source_slice_layout);
TileConvertTiledToLinearRenderTarget(
dst, src, regions[region_index].width, regions[region_index].height,
regions[region_index].pitch,
Prospero::RenderTargetBytesPerElement(static_cast<uint32_t>(fmt)),
layout.level_sizes[i].size, GetLevelSrcSize(layout.level_sizes[i]),
layout.level_sizes[i].x, layout.level_sizes[i].y);
}
}
Transfer::UploadImage(ctx, vk_obj, temp_buf.Data(), size, regions, dst_layout);
} else if (layout.fmt_tiled_depth) {
if (Prospero::IsFmaskTextureFormat(static_cast<uint32_t>(fmt))) {
UploadFmaskIdentity(ctx, vk_obj, regions, dst_layout, owner);
return;
}
const uint32_t bytes_per_element =
Prospero::RenderTargetBytesPerElement(static_cast<uint32_t>(fmt));
if (bytes_per_element == 1) {
Transfer::UploadImage(ctx, vk_obj, src_data, size, regions, dst_layout);
} else {
LOGF("%s: detiling typed depth texture: fmt=%u tile=%u size=%" PRIu64 " extent=%" PRIu64
"x%" PRIu64 " depth=%" PRIu64 " pitch=%u levels=%" PRIu64 "\n",
owner, static_cast<uint32_t>(fmt), layout.tile, size, width, height,
static_cast<uint64_t>(depth), layout.pitch, levels);
Transfer::ScratchBuffer temp_buf(size);
for (uint32_t i = 0; i < levels; i++) {
for (uint32_t z = 0; z < depth; z++) {
const auto region_index = i * depth + z;
auto* dst =
static_cast<uint8_t*>(temp_buf.Data()) + regions[region_index].offset;
const auto* src = static_cast<const uint8_t*>(src_data) +
GetLevelSrcOffset(layout.level_sizes[i]) +
z * GetSliceSrcStride(layout, i, source_slice_layout);
TileConvertTiledToLinearDepth(
dst, src, static_cast<uint32_t>(fmt), regions[region_index].width,
regions[region_index].height, regions[region_index].pitch,
layout.level_sizes[i].size);
}
}
Transfer::UploadImage(ctx, vk_obj, temp_buf.Data(), size, regions, dst_layout);
}
} else if (layout.fmt_tiled_standard256b) {
Transfer::ScratchBuffer temp_buf(size);
for (uint32_t i = 0; i < levels; i++) {
for (uint32_t z = 0; z < depth; z++) {
const auto region_index = i * depth + z;
auto* dst = static_cast<uint8_t*>(temp_buf.Data()) + regions[region_index].offset;
const auto* src = static_cast<const uint8_t*>(src_data) +
layout.level_sizes[i].offset +
z * GetSliceSrcStride(layout, i, source_slice_layout);
TileConvertTiledToLinearStandard256B(
dst, src, static_cast<uint32_t>(fmt), regions[region_index].width,
regions[region_index].height, regions[region_index].pitch,
layout.level_sizes[i].size, layout.level_sizes[i].size);
}
}
Transfer::UploadImage(ctx, vk_obj, temp_buf.Data(), size, regions, dst_layout);
} else if (layout.fmt_tiled_standard4kb && layout.volume_texture) {
LOGF("%s: detiling typed Standard4KB 3D texture: fmt=%u tile=%u size=%" PRIu64
" extent=%" PRIu64 "x%" PRIu64 " depth=%" PRIu64 " pitch=%u levels=%" PRIu64 "\n",
owner, static_cast<uint32_t>(fmt), layout.tile, size, width, height,
static_cast<uint64_t>(depth), layout.pitch, levels);
Transfer::ScratchBuffer temp_buf(size);
if (levels == 1) {
TileConvertTiledToLinearStandard4KB3D(
temp_buf.Data(), src_data, static_cast<uint32_t>(fmt), static_cast<uint32_t>(width),
static_cast<uint32_t>(height), depth, layout.pitch, layout.slice_stride, size,
size);
} else {
std::memset(temp_buf.Data(), 0, static_cast<size_t>(size));
Standard4KBVolumeMipLayout volume_layout {};
EXIT_NOT_IMPLEMENTED(!CalcStandard4kbVolumeMipLayout(
static_cast<uint32_t>(fmt), layout.pitch, static_cast<uint32_t>(height), levels,
&volume_layout));
auto* dst_base = static_cast<uint8_t*>(temp_buf.Data());
const auto* src_base = static_cast<const uint8_t*>(src_data);
uint32_t mip_width = static_cast<uint32_t>(width);
uint32_t mip_height = static_cast<uint32_t>(height);
uint32_t mip_pitch = layout.pitch;
for (uint32_t level = 0; level < levels; level++) {
if (level < volume_layout.first_tail_level) {
for (uint32_t z = 0; z < depth; z += volume_layout.block_depth) {
const uint32_t copy_depth = std::min(volume_layout.block_depth, depth - z);
const auto region_index = level * depth + z;
auto* dst = dst_base + regions[region_index].offset;
const auto* src = src_base +
(static_cast<uint64_t>(z / volume_layout.block_depth) *
volume_layout.block_slice_size) +
volume_layout.level_offsets[level];
const uint64_t dst_size =
(static_cast<uint64_t>(copy_depth - 1u) * layout.slice_stride) +
layout.level_sizes[level].size;
TileConvertTiledToLinearStandard4KB3D(
dst, src, static_cast<uint32_t>(fmt), mip_width, mip_height, copy_depth,
mip_pitch, layout.slice_stride, dst_size,
volume_layout.level_sizes[level], false);
}
}
if (mip_width > 1) {
mip_width /= 2;
}
if (mip_height > 1) {
mip_height /= 2;
}
if (mip_pitch > 1) {
mip_pitch /= 2;
}
}
}
Transfer::UploadImage(ctx, vk_obj, temp_buf.Data(), size, regions, dst_layout);
} else if (layout.fmt_tiled_standard4kb) {
LOGF("%s: detiling typed Standard4KB texture: fmt=%u tile=%u size=%" PRIu64
" extent=%" PRIu64 "x%" PRIu64 " depth=%" PRIu64 " pitch=%u\n",
owner, static_cast<uint32_t>(fmt), layout.tile, size, width, height,
static_cast<uint64_t>(depth), layout.pitch);
Transfer::ScratchBuffer temp_buf(size);
for (uint32_t i = 0; i < levels; i++) {
for (uint32_t z = 0; z < depth; z++) {
const auto region_index = i * depth + z;
auto* dst = static_cast<uint8_t*>(temp_buf.Data()) + regions[region_index].offset;
const auto* src = static_cast<const uint8_t*>(src_data) +
GetLevelSrcOffset(layout.level_sizes[i]) +
z * GetSliceSrcStride(layout, i, source_slice_layout);
TileConvertTiledToLinearStandard4KB(
dst, src, static_cast<uint32_t>(fmt), regions[region_index].width,
regions[region_index].height, regions[region_index].pitch,
layout.level_sizes[i].size, GetLevelSrcSize(layout.level_sizes[i]),
layout.level_sizes[i].x, layout.level_sizes[i].y);
}
}
Transfer::UploadImage(ctx, vk_obj, temp_buf.Data(), size, regions, dst_layout);
} else if (layout.fmt_tiled_standard64kb) {
LOGF("%s: detiling typed Standard64KB texture: fmt=%u tile=%u size=%" PRIu64
" extent=%" PRIu64 "x%" PRIu64 " depth=%" PRIu64 " pitch=%u\n",
owner, static_cast<uint32_t>(fmt), layout.tile, size, width, height,
static_cast<uint64_t>(depth), layout.pitch);
Transfer::ScratchBuffer temp_buf(size);
for (uint32_t i = 0; i < levels; i++) {
for (uint32_t z = 0; z < depth; z++) {
const auto region_index = i * depth + z;
auto* dst = static_cast<uint8_t*>(temp_buf.Data()) + regions[region_index].offset;
const auto* src = static_cast<const uint8_t*>(src_data) +
GetLevelSrcOffset(layout.level_sizes[i]) +
z * GetSliceSrcStride(layout, i, source_slice_layout);
TileConvertTiledToLinearStandard64KB(
dst, src, static_cast<uint32_t>(fmt), regions[region_index].width,
regions[region_index].height, regions[region_index].pitch,
layout.level_sizes[i].size, GetLevelSrcSize(layout.level_sizes[i]),
layout.level_sizes[i].x, layout.level_sizes[i].y);
}
}
Transfer::UploadImage(ctx, vk_obj, temp_buf.Data(), size, regions, dst_layout);
} else if (layout.tile != 0) {
EXIT("%s: typed tiled upload still unsupported after sizing, using linear fallback: fmt=%u "
"tile=%u size=%" PRIu64 " extent=%" PRIu64 "x%" PRIu64 " pitch=%u levels=%" PRIu64
"\n",
owner, static_cast<uint32_t>(fmt), layout.tile, size, width, height, layout.pitch,
levels);
Transfer::UploadImage(ctx, vk_obj, src_data, size, regions, dst_layout);
}
if (static_cast<Prospero::TileMode>(layout.tile) == Prospero::TileMode::kLinear) {
Transfer::UploadImage(ctx, vk_obj, src_data, size, regions, dst_layout);
return;
}
if (layout.tile_family == TileBlockFamily::Depth64KB && Prospero::IsFmaskTextureFormat(fmt)) {
UploadFmaskIdentity(ctx, vk_obj, regions, dst_layout, owner);
return;
}
std::vector<GpuTileInfo> infos;
if (!TextureBuildGpuTileInfos(size, regions, layout, fmt, depth, levels, &infos)) {
EXIT("%s: GPU tiled upload unsupported: fmt=%u tile=%u size=%" PRIu64 " extent=%" PRIu64
"x%" PRIu64 " depth=%u pitch=%u levels=%" PRIu64 "\n",
owner, fmt, layout.tile, size, width, height, depth, layout.pitch, levels);
}
Transfer::UploadTiledImage(ctx, vk_obj, src_data, size, size, infos, regions, dst_layout);
}
} // namespace Libs::Graphics
+25 -29
View File
@@ -44,8 +44,6 @@ constexpr bool TextureHasFormatUsage(TextureFormatUsage usage, TextureFormatUsag
enum class TextureUploadDestination { MipLevels, MipAtlas };
enum class TextureUploadSliceLayout { MipChainPerSlice, MipLevelPerSlice };
struct RenderTargetFormatInfo {
vk::Format format = vk::Format::eUndefined;
uint32_t bytes_per_element = 0;
@@ -53,18 +51,15 @@ struct RenderTargetFormatInfo {
};
struct TextureUploadLayout {
uint32_t tile = 0;
uint32_t pitch = 0;
uint64_t slice_stride = 0;
uint64_t source_slice_stride = 0;
bool fmt_tiled_render_target = false;
bool fmt_tiled_standard256b = false;
bool fmt_tiled_standard4kb = false;
bool fmt_tiled_standard64kb = false;
bool fmt_tiled_depth = false;
bool volume_texture = false;
TileSizeOffset level_sizes[16] = {};
TilePaddedSize padded_sizes[16] = {};
uint32_t tile = 0;
uint32_t pitch = 0;
uint64_t slice_stride = 0;
uint64_t source_slice_stride = 0;
uint32_t first_tail_level = 16;
TileBlockFamily tile_family = TileBlockFamily::Count;
bool volume_texture = false;
TileSizeOffset level_sizes[16] = {};
TilePaddedSize padded_sizes[16] = {};
};
struct TextureImageCreateParams {
@@ -113,27 +108,28 @@ void TextureCreateImageViews(GraphicContext* ctx, VulkanImage* vk_obj,
TextureUploadLayout TextureCalcUploadLayout(uint32_t fmt, uint64_t width, uint64_t height,
uint64_t levels, uint32_t depth, uint64_t pitch,
uint64_t tile, uint64_t upload_size,
bool allow_depth_tile,
bool require_single_mip_small_tiles,
bool volume_texture, const char* owner);
bool allow_depth_tile, bool volume_texture,
const char* owner);
uint64_t TextureUploadSliceSourceOffset(const TextureUploadLayout& layout, uint32_t level,
uint32_t slice,
TextureUploadSliceLayout source_slice_layout);
uint32_t slice);
uint64_t TextureCalcUploadSize(const TextureUploadLayout& layout,
const std::vector<BufferImageCopy>& regions, uint64_t levels,
uint32_t depth, TextureUploadSliceLayout source_slice_layout);
std::vector<BufferImageCopy> TextureBuildUploadRegions(
const TextureUploadLayout& layout, vk::Format image_format, uint32_t width, uint32_t height,
uint32_t depth, uint64_t levels, bool array_texture, bool volume_texture,
TextureUploadDestination destination, TextureUploadSliceLayout slice_layout);
void TextureCopyBufferBytes(GraphicContext* ctx, VulkanBuffer* src_buffer,
uint64_t src_buffer_offset, uint64_t copy_size,
Transfer::ScratchBuffer* dst);
uint32_t depth);
std::vector<BufferImageCopy> TextureBuildUploadRegions(const TextureUploadLayout& layout,
vk::Format image_format, uint32_t width,
uint32_t height, uint32_t depth,
uint64_t levels, bool array_texture,
bool volume_texture,
TextureUploadDestination destination);
std::vector<ImageBufferCopy>
TextureBuildDownloadRegions(const std::vector<BufferImageCopy>& upload_regions);
bool TextureBuildGpuTileInfos(uint64_t size, const std::vector<BufferImageCopy>& regions,
const TextureUploadLayout& layout, uint32_t fmt, uint32_t depth,
uint64_t levels, std::vector<GpuTileInfo>* infos);
void TextureUploadGuestImage(GraphicContext* ctx, VulkanImage* vk_obj, const void* src_data,
uint64_t size, const std::vector<BufferImageCopy>& regions,
const TextureUploadLayout& layout, uint32_t fmt, uint64_t width,
uint64_t height, uint32_t depth, uint64_t levels,
TextureUploadSliceLayout source_slice_layout, const char* owner,
uint64_t height, uint32_t depth, uint64_t levels, const char* owner,
vk::ImageLayout dst_layout);
} // namespace Libs::Graphics
+54 -24
View File
@@ -4,6 +4,7 @@
#include "common/profiler.h"
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/gpuTiler.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/objects/textureCommon.h"
#include "graphics/host_gpu/regionDefinitions.h"
@@ -191,36 +192,39 @@ void UploadRenderTargetLayers(GraphicContext* ctx, RenderTextureVulkanImage* ima
const auto format = RenderTargetTransferFormat(info.bytes_per_element);
auto layout = TextureCalcUploadLayout(format, info.width, info.height, info.levels,
layer_count, info.pitch, info.tile_mode, upload_size,
false, false, false, "TextureCache render target");
false, false, "TextureCache render target");
const bool render_target_tiled =
info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
if (!standard64 && ((render_target_tiled && !layout.fmt_tiled_render_target) ||
layout.pitch != info.pitch)) {
if (!standard64 &&
((render_target_tiled && layout.tile_family != TileBlockFamily::RenderTarget64KB) ||
layout.pitch != info.pitch)) {
EXIT("TextureCache: unsupported render-target mip upload layout, pitch=%u/%u tile=%u\n",
info.pitch, layout.pitch, info.tile_mode);
}
auto regions = TextureBuildUploadRegions(
layout, info.format, info.width, info.height, layer_count, info.levels, true, false,
TextureUploadDestination::MipLevels, TextureUploadSliceLayout::MipChainPerSlice);
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height,
layer_count, info.levels, true, false,
TextureUploadDestination::MipLevels);
for (auto& region: regions) {
region.dst_layer += base_layer;
}
const auto source_address = info.address + slice_size * base_layer;
TextureUploadGuestImage(ctx, image, reinterpret_cast<const void*>(source_address),
upload_size, regions, layout, format, info.width, info.height,
layer_count, info.levels,
TextureUploadSliceLayout::MipChainPerSlice,
"TextureCache render target", vk::ImageLayout::eGeneral);
layer_count, info.levels, "TextureCache render target",
vk::ImageLayout::eGeneral);
return;
}
if (info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) &&
Transfer::GuestBufferIsTiled(info.address, slice_size)) {
Transfer::ScratchBuffer scratch(slice_size);
TileConvertTiledToLinearRenderTarget(
scratch.Data(), reinterpret_cast<const void*>(info.address), info.width, info.height,
info.pitch, info.bytes_per_element, slice_size);
Transfer::UploadImage(ctx, image, scratch.Data(), slice_size, info.pitch,
vk::ImageLayout::eGeneral);
const auto format = RenderTargetTransferFormat(info.bytes_per_element);
auto layout = TextureCalcUploadLayout(format, info.width, info.height, 1, 1, info.pitch,
info.tile_mode, slice_size, false, false,
"TextureCache render target");
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
true, false, TextureUploadDestination::MipLevels);
TextureUploadGuestImage(ctx, image, reinterpret_cast<const void*>(info.address), slice_size,
regions, layout, format, info.width, info.height, 1, 1,
"TextureCache render target", vk::ImageLayout::eGeneral);
} else {
Transfer::UploadImage(ctx, image, reinterpret_cast<const void*>(info.address), slice_size,
info.pitch, vk::ImageLayout::eGeneral);
@@ -383,20 +387,46 @@ void UploadVideoOut(GraphicContext* ctx, VideoOutVulkanImage* image, const Video
Transfer::WaitForGraphicsIdle(ctx);
}
image->layout = vk::ImageLayout::eUndefined;
Transfer::ScratchBuffer scratch(info.size);
TileConvertTiledToLinearRenderTarget(
scratch.Data(), reinterpret_cast<const void*>(info.address), info.width, info.height,
info.pitch, info.bytes_per_element, info.size);
if (info.bgra16) {
auto* pixels = static_cast<uint16_t*>(scratch.Data());
for (uint64_t i = 0; i < info.size / sizeof(uint16_t); i += 4) {
std::swap(pixels[i], pixels[i + 2]);
}
if (!info.bgra16) {
auto layout =
TextureCalcUploadLayout(info.guest_format, info.width, info.height, 1, 1, info.pitch,
info.tile_mode, info.size, false, false, "VideoOut");
auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1,
false, false, TextureUploadDestination::MipLevels);
TextureUploadGuestImage(ctx, image, reinterpret_cast<const void*>(info.address), info.size,
regions, layout, info.guest_format, info.width, info.height, 1, 1,
"VideoOut", vk::ImageLayout::eGeneral);
return;
}
Transfer::ScratchBuffer scratch(info.size);
TileBlockLayout block {};
EXIT_NOT_IMPLEMENTED(
!TileGetBlockLayout(TileBlockFamily::RenderTarget64KB, info.bytes_per_element, &block));
const GpuTileInfo tile_info {block.family,
block.bytes_per_element,
0,
info.size,
0,
info.size,
0,
info.width,
info.height,
1,
info.pitch};
GpuDetile(ctx, reinterpret_cast<const void*>(info.address), scratch.Data(), info.size,
info.size, std::span<const GpuTileInfo>(&tile_info, 1));
SwapVideoOutBgra16(scratch.Data(), info.size);
Transfer::UploadImage(ctx, image, scratch.Data(), info.size, info.pitch,
vk::ImageLayout::eGeneral);
}
void SwapVideoOutBgra16(void* data, uint64_t size) {
auto* pixels = static_cast<uint16_t*>(data);
for (uint64_t i = 0; i < size / sizeof(uint16_t); i += 4) {
std::swap(pixels[i], pixels[i + 2]);
}
}
GpuTextureVulkanImage* CreateDummyTexture(GraphicContext* ctx, bool uint_format, bool image_3d,
bool storage) {
auto* image = storage ? static_cast<GpuTextureVulkanImage*>(new StorageTextureVulkanImage)
+1
View File
@@ -119,6 +119,7 @@ void UploadRenderTarget(GraphicContext* ctx, RenderTextureVulkanImage* image,
void ValidateVideoOut(GraphicContext* ctx, const VideoOutInfo& info);
[[nodiscard]] VideoOutVulkanImage* CreateVideoOut(GraphicContext* ctx, const VideoOutInfo& info);
void SwapVideoOutBgra16(void* data, uint64_t size);
void UploadVideoOut(GraphicContext* ctx, VideoOutVulkanImage* image, const VideoOutInfo& info,
bool refresh);
+158 -102
View File
@@ -7,6 +7,7 @@
#include "graphics/guest_gpu/gpu_format.h"
#include "graphics/guest_gpu/graphicsRun.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/gpuTiler.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/objects/label.h"
#include "graphics/host_gpu/objects/textureCommon.h"
@@ -587,7 +588,6 @@ struct TextureCache::ReadbackWorker {
cached.image->extent.height, info.width, info.height, meta_overlap,
buffer_overlap);
}
download.resize(transfer_size);
auto regions = Transfer::MakeLayeredImageBufferCopies(info.layers, depth_slice_size,
info.pitch, info.width, info.height,
vk::ImageAspectFlagBits::eDepth);
@@ -600,17 +600,55 @@ struct TextureCache::ReadbackWorker {
}
regions.insert(regions.end(), stencil_regions.begin(), stencil_regions.end());
}
Transfer::DownloadImage(cached.ctx, download.data(), transfer_size, regions, cached.image,
cached.image->layout);
guest.resize(info.size);
cache.m_tiler.TileImage(guest.data(), download.data(), info);
std::vector<GpuTileInfo> gpu_infos;
TileBlockLayout depth_block {};
TileBlockLayout stencil_block {};
EXIT_NOT_IMPLEMENTED(
!TileGetBlockLayout(TileBlockFamily::Depth64KB, info.bytes_per_element, &depth_block) ||
(has_stencil && !TileGetBlockLayout(TileBlockFamily::Depth64KB, 1, &stencil_block)));
gpu_infos.reserve(regions.size());
for (uint32_t layer = 0; layer < info.layers; layer++) {
const uint64_t offset = depth_slice_size * layer;
GpuTileInfo tile {depth_block.family,
depth_block.bytes_per_element,
offset,
depth_slice_size,
offset,
depth_slice_size,
0,
info.width,
info.height,
1,
info.pitch};
tile.surface_z = layer;
gpu_infos.push_back(tile);
}
if (has_stencil) {
for (uint32_t layer = 0; layer < info.layers; layer++) {
const uint64_t offset = info.size + stencil_slice_size * layer;
GpuTileInfo tile {stencil_block.family,
stencil_block.bytes_per_element,
offset,
stencil_slice_size,
offset,
stencil_slice_size,
0,
info.width,
info.height,
1,
expected_stencil_pitch};
tile.surface_z = layer;
gpu_infos.push_back(tile);
}
}
guest.resize(transfer_size);
Transfer::DownloadTiledImage(cached.ctx, guest.data(), transfer_size, transfer_size,
gpu_infos, regions, cached.image, cached.image->layout);
Libs::LibKernel::Memory::WriteBacking(info.address, guest.data(), info.size);
ReadbackTransfer transfer;
transfer.Add(info.address, info.size);
if (has_stencil) {
guest.resize(info.stencil_size);
cache.m_tiler.TileStencil(guest.data(), download.data() + info.size, info);
Libs::LibKernel::Memory::WriteBacking(info.stencil_address, guest.data(),
Libs::LibKernel::Memory::WriteBacking(info.stencil_address, guest.data() + info.size,
info.stencil_size);
transfer.Add(info.stencil_address, info.stencil_size);
}
@@ -672,66 +710,48 @@ struct TextureCache::ReadbackWorker {
info.address, info.size, meta_overlap, buffer_overlap,
static_cast<uint32_t>(cached.kind));
}
download.resize(info.size);
std::fill(download.begin(), download.end(), 0);
std::vector<ImageBufferCopy> regions;
if (target_mip_chain) {
const auto format = ImageOps::RenderTargetTransferFormat(info.bytes_per_element);
auto layout = TextureCalcUploadLayout(format, info.width, info.height, info.levels, 1,
info.pitch, info.tile_mode, info.size, false,
false, false, "RenderTargetReadback");
if (!layout.fmt_tiled_render_target || layout.pitch != info.pitch) {
std::vector<BufferImageCopy> tiled_regions;
TextureUploadLayout tiled_layout {};
const bool gpu_tiled = tiled_target || tiled_storage;
if (gpu_tiled) {
const auto format = storage
? cached.info.format
: ImageOps::RenderTargetTransferFormat(info.bytes_per_element);
tiled_layout = TextureCalcUploadLayout(format, info.width, info.height, info.levels,
layers, info.pitch, info.tile_mode, info.size,
false, false, "RenderTargetReadback");
if (target_mip_chain &&
(tiled_layout.tile_family != TileBlockFamily::RenderTarget64KB ||
tiled_layout.pitch != info.pitch)) {
EXIT("TextureCache: inconsistent render-target readback layout, addr=0x%016" PRIx64
" size=0x%016" PRIx64 " pitch=%u/%u levels=%u\n",
info.address, info.size, info.pitch, layout.pitch, info.levels);
}
const auto uploads = TextureBuildUploadRegions(
layout, info.format, info.width, info.height, 1, info.levels, true, false,
TextureUploadDestination::MipLevels, TextureUploadSliceLayout::MipChainPerSlice);
regions.reserve(uploads.size());
for (const auto& upload: uploads) {
regions.push_back({upload.offset, upload.pitch, upload.dst_level, upload.width,
upload.height, upload.copy_height, upload.dst_layer,
upload.dst_x, upload.dst_y, upload.dst_z, upload.aspect});
info.address, info.size, info.pitch, tiled_layout.pitch, info.levels);
}
tiled_regions = TextureBuildUploadRegions(tiled_layout, info.format, info.width,
info.height, layers, info.levels, layers > 1,
false, TextureUploadDestination::MipLevels);
regions = TextureBuildDownloadRegions(tiled_regions);
} else {
regions = Transfer::MakeLayeredImageBufferCopies(layers, slice_size, info.pitch,
info.width, info.height);
}
Transfer::DownloadImage(cached.ctx, download.data(), info.size, regions, cached.image,
cached.image->layout);
if (target_mip_chain) {
if (gpu_tiled) {
std::vector<GpuTileInfo> infos;
const auto format = storage
? cached.info.format
: ImageOps::RenderTargetTransferFormat(info.bytes_per_element);
guest.resize(info.size);
ImageInfo layout {};
layout.address = info.address;
layout.size = info.size;
layout.format = ImageOps::RenderTargetTransferFormat(info.bytes_per_element);
layout.width = info.width;
layout.height = info.height;
layout.pitch = info.pitch;
layout.levels = info.levels;
layout.view_levels = info.levels;
layout.tile = info.tile_mode;
layout.depth = 1;
layout.type = Prospero::GpuEnumValue(Prospero::ImageType::kColor2D);
cache.m_tiler.TileImage(guest.data(), download.data(), layout);
Libs::LibKernel::Memory::WriteBacking(info.address, guest.data(), info.size);
} else if (tiled_target || tiled_storage) {
guest.resize(info.size);
const RenderTargetInfo layout =
target ? cached.target : RenderTargetInfo {info.address,
info.size,
info.format,
info.width,
info.height,
info.pitch,
info.bytes_per_element,
info.tile_mode,
info.levels,
1};
cache.m_tiler.TileImage(guest.data(), download.data(), layout);
EXIT_NOT_IMPLEMENTED(!TextureBuildGpuTileInfos(info.size, tiled_regions, tiled_layout,
format, layers, info.levels, &infos));
Transfer::DownloadTiledImage(cached.ctx, guest.data(), info.size, info.size, infos,
regions, cached.image, cached.image->layout);
Libs::LibKernel::Memory::WriteBacking(info.address, guest.data(), info.size);
} else {
download.resize(info.size);
std::fill(download.begin(), download.end(), 0);
Transfer::DownloadImage(cached.ctx, download.data(), info.size, regions, cached.image,
cached.image->layout);
Libs::LibKernel::Memory::WriteBacking(info.address, download.data(), info.size);
}
ReadbackTransfer transfer;
@@ -1061,8 +1081,7 @@ bool Equal(const DepthTargetInfo& left, const DepthTargetInfo& right) {
[[nodiscard]] bool IsCoherentGuestImageSource(const BufferImageCopySource& source, uint64_t address,
uint64_t size) {
// The current PS5 Tiler consumes coherent guest backing directly. A native buffer is an
// optional future GPU-detiler source or staging fallback.
// The GPU tiler currently consumes coherent guest backing directly.
return source.cpu_current && source.address == address && source.size == size &&
(source.buffer != nullptr || source.offset == 0);
}
@@ -1384,8 +1403,7 @@ VulkanImage* TextureCache::FindTexture(CommandBuffer* command, GraphicContext* c
}
BufferImageCopySource source {nullptr, 0, info.address, info.size, true};
if (m_buffer_cache.HasPageOverlap(info.address, info.size)) {
// ObtainBufferForImage publishes dirty native-buffer bytes when necessary and otherwise
// uses a CPU-current staging fallback through guest backing.
// ObtainBufferForImage publishes dirty native-buffer bytes into coherent guest backing.
source = m_buffer_cache.ObtainBufferForImage(info.address, info.size);
if (!IsCoherentGuestImageSource(source, info.address, info.size)) {
EXIT("TextureCache: sampled-image buffer source is inconsistent, addr=0x%016" PRIx64
@@ -2963,6 +2981,7 @@ void TextureCache::SynchronizeColorImageToBufferLocked(CachedImage& cached, uint
}
const bool linear = target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kLinear);
const bool tiled = IsTiledRenderTarget(target);
const bool bgra16 = video_out && cached.video_out.bgra16;
TileSizeAlign exact {};
bool single_slice = false;
if (IsSupportedStandard64RenderTarget(target)) {
@@ -3014,47 +3033,72 @@ void TextureCache::SynchronizeColorImageToBufferLocked(CachedImage& cached, uint
target.address, target.size);
}
// This is the CPU Tiler backend for the image-to-buffer synchronization seam.
// The guest vector and Vulkan staging allocation retain capacity; a future PS5 GPU tiler can
// replace this block without changing alias classification or ownership transitions.
Transfer::WaitForGraphicsIdle(cached.ctx);
std::vector<ImageBufferCopy> regions;
std::vector<BufferImageCopy> tiled_regions;
TextureUploadLayout tiled_layout {};
uint32_t tiled_format = 0;
if (storage) {
auto layout = TextureCalcUploadLayout(
const bool array_texture = TextureIsLayeredTexture(cached.info.type);
const bool volume_texture = TextureIs3DTexture(cached.info.type);
tiled_format = cached.info.format;
tiled_layout = TextureCalcUploadLayout(
cached.info.format, cached.info.width, cached.info.height, cached.info.levels,
cached.info.depth, cached.info.pitch, cached.info.tile, cached.info.size, true, false,
false, "StorageTextureReadback");
auto uploads = TextureBuildUploadRegions(
layout, cached.image->format, cached.info.width, cached.info.height, cached.info.depth,
cached.info.levels, false, false, TextureUploadDestination::MipLevels,
TextureUploadSliceLayout::MipChainPerSlice);
regions.reserve(uploads.size());
for (const auto& upload: uploads) {
regions.push_back({upload.offset, upload.pitch, upload.dst_level, upload.width,
upload.height, upload.copy_height, upload.dst_layer, upload.dst_x,
upload.dst_y, upload.dst_z, upload.aspect});
}
cached.info.depth, cached.info.pitch, cached.info.tile, cached.info.size, true,
volume_texture, "StorageTextureReadback");
tiled_regions = TextureBuildUploadRegions(
tiled_layout, cached.image->format, cached.info.width, cached.info.height,
cached.info.depth, cached.info.levels, array_texture, volume_texture,
TextureUploadDestination::MipLevels);
regions = TextureBuildDownloadRegions(tiled_regions);
} else if (tiled && !bgra16) {
tiled_format = ImageOps::RenderTargetTransferFormat(target.bytes_per_element);
tiled_layout = TextureCalcUploadLayout(
tiled_format, target.width, target.height, target.levels, target.layers, target.pitch,
target.tile_mode, target.size, false, false, "ColorBufferTransition");
tiled_regions = TextureBuildUploadRegions(
tiled_layout, target.format, target.width, target.height, target.layers, target.levels,
target.layers > 1, false, TextureUploadDestination::MipLevels);
regions = TextureBuildDownloadRegions(tiled_regions);
} else {
regions = Transfer::MakeLayeredImageBufferCopies(target.layers, slice_size, target.pitch,
target.width, target.height);
}
Transfer::ProcessDownloadedImage(
cached.ctx, target.size, regions, cached.image, cached.image->layout,
[&](std::span<const uint8_t> linear) {
if (storage) {
m_buffer_transition_guest.resize(target.size);
m_tiler.TileImage(m_buffer_transition_guest.data(), linear.data(), cached.info);
Libs::LibKernel::Memory::WriteBacking(
target.address, m_buffer_transition_guest.data(), target.size);
} else if (tiled) {
m_buffer_transition_guest.resize(target.size);
m_tiler.TileImage(m_buffer_transition_guest.data(), linear.data(), target);
Libs::LibKernel::Memory::WriteBacking(
target.address, m_buffer_transition_guest.data(), target.size);
} else {
Libs::LibKernel::Memory::WriteBacking(target.address, linear.data(), target.size);
}
});
if (tiled && !bgra16) {
std::vector<GpuTileInfo> infos;
const uint32_t depth = storage ? cached.info.depth : target.layers;
EXIT_NOT_IMPLEMENTED(!TextureBuildGpuTileInfos(target.size, tiled_regions, tiled_layout,
tiled_format, depth, target.levels, &infos));
m_buffer_transition_guest.resize(target.size);
Transfer::DownloadTiledImage(cached.ctx, m_buffer_transition_guest.data(), target.size,
target.size, infos, regions, cached.image,
cached.image->layout);
Libs::LibKernel::Memory::WriteBacking(target.address, m_buffer_transition_guest.data(),
target.size);
} else if (tiled) {
std::vector<uint8_t> linear_data(target.size);
Transfer::DownloadImage(cached.ctx, linear_data.data(), target.size, regions, cached.image,
cached.image->layout);
ImageOps::SwapVideoOutBgra16(linear_data.data(), target.size);
TileBlockLayout block {};
EXIT_NOT_IMPLEMENTED(!TileGetBlockLayout(TileBlockFamily::RenderTarget64KB,
target.bytes_per_element, &block));
const GpuTileInfo info {
block.family, block.bytes_per_element, 0, target.size, 0, target.size, 0,
target.width, target.height, 1, target.pitch};
m_buffer_transition_guest.resize(target.size);
GpuTile(cached.ctx, linear_data.data(), m_buffer_transition_guest.data(), target.size,
target.size, std::span<const GpuTileInfo>(&info, 1));
Libs::LibKernel::Memory::WriteBacking(target.address, m_buffer_transition_guest.data(),
target.size);
} else {
Transfer::ProcessDownloadedImage(cached.ctx, target.size, regions, cached.image,
cached.image->layout,
[&](std::span<const uint8_t> linear_data) {
Libs::LibKernel::Memory::WriteBacking(
target.address, linear_data.data(), target.size);
});
}
m_memory_tracker.ForEachDownloadRange<true>(target.address, target.size,
[](uint64_t, uint64_t) noexcept {});
// Only the impending buffer-write range needs publication into BufferCache ownership. The
@@ -3103,14 +3147,26 @@ void TextureCache::SynchronizeDepthImageToBufferLocked(CachedImage& cached, uint
Transfer::WaitForGraphicsIdle(cached.ctx);
const auto regions = Transfer::MakeLayeredImageBufferCopies(
1, info.size, info.pitch, info.width, info.height, vk::ImageAspectFlagBits::eDepth);
Transfer::ProcessDownloadedImage(
cached.ctx, info.size, regions, cached.image, cached.image->layout,
[&](std::span<const uint8_t> linear) {
m_buffer_transition_guest.resize(info.size);
m_tiler.TileImage(m_buffer_transition_guest.data(), linear.data(), info);
Libs::LibKernel::Memory::WriteBacking(info.address, m_buffer_transition_guest.data(),
info.size);
});
TileBlockLayout block {};
EXIT_NOT_IMPLEMENTED(
!TileGetBlockLayout(TileBlockFamily::Depth64KB, info.bytes_per_element, &block));
const GpuTileInfo tile_info {block.family,
block.bytes_per_element,
0,
info.size,
0,
info.size,
0,
info.width,
info.height,
1,
info.pitch};
m_buffer_transition_guest.resize(info.size);
Transfer::DownloadTiledImage(cached.ctx, m_buffer_transition_guest.data(), info.size, info.size,
std::span<const GpuTileInfo>(&tile_info, 1), regions, cached.image,
cached.image->layout);
Libs::LibKernel::Memory::WriteBacking(info.address, m_buffer_transition_guest.data(),
info.size);
m_memory_tracker.ForEachDownloadRange<true>(info.address, info.size,
[](uint64_t, uint64_t) noexcept {});
m_buffer_cache.PublishImageBacking(write_address, write_size);
+101 -195
View File
@@ -4,86 +4,132 @@
#include "graphics/guest_gpu/gpu_defs.h"
#include "graphics/guest_gpu/gpu_format.h"
#include "graphics/guest_gpu/tile.h"
#include "graphics/host_gpu/gpuTiler.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/objects/textureCommon.h"
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/transfer.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <vector>
namespace Libs::Graphics {
namespace {
struct DepthTransfer {
std::vector<GpuTileInfo> infos;
std::vector<BufferImageCopy> regions;
};
DepthTransfer MakeDepthTransfer(uint64_t size, uint32_t layers, uint32_t format,
uint32_t bytes_per_element, uint32_t width, uint32_t height,
uint32_t pitch, uint32_t base_layer, vk::ImageAspectFlags aspect) {
EXIT_IF(size == 0 || layers == 0 || size % layers != 0);
TileBlockLayout block {};
EXIT_NOT_IMPLEMENTED(
!TileGetBlockLayout(TileBlockFamily::Depth64KB, bytes_per_element, &block) ||
Prospero::NumBytesPerElement(format) != bytes_per_element);
const uint64_t slice_size = size / layers;
DepthTransfer transfer;
transfer.infos.reserve(layers);
transfer.regions.reserve(layers);
for (uint32_t layer = 0; layer < layers; layer++) {
const uint64_t offset = slice_size * layer;
GpuTileInfo info {block.family,
block.bytes_per_element,
offset,
slice_size,
offset,
slice_size,
0,
width,
height,
1,
pitch};
info.surface_z = base_layer + layer;
transfer.infos.push_back(info);
BufferImageCopy region {};
region.offset = static_cast<uint32_t>(offset);
region.pitch = pitch;
region.width = width;
region.height = height;
region.dst_layer = base_layer + layer;
region.aspect = aspect;
transfer.regions.push_back(region);
}
return transfer;
}
void UploadDepth(GraphicContext* ctx, DepthStencilVulkanImage* image, uint64_t source_address,
uint64_t size, uint32_t layers, uint32_t format, uint32_t bytes_per_element,
uint32_t width, uint32_t height, uint32_t pitch, uint32_t base_layer,
vk::ImageAspectFlags aspect) {
auto transfer = MakeDepthTransfer(size, layers, format, bytes_per_element, width, height, pitch,
base_layer, aspect);
Transfer::UploadTiledImage(ctx, image, reinterpret_cast<const void*>(source_address), size,
size, transfer.infos, transfer.regions,
vk::ImageLayout::eDepthStencilAttachmentOptimal);
}
template <uint32_t (*Encode)(uint16_t)>
static void UploadPromotedD16Depth(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
uint32_t base_layer) {
const uint64_t slice_size = info.size / info.layers;
void UploadPromotedD16Depth(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
uint32_t base_layer) {
const uint64_t guest_slice_size = info.size / info.layers;
const uint64_t texels = static_cast<uint64_t>(info.pitch) * info.height;
const uint64_t host_slice_size = texels * sizeof(uint32_t);
const uint64_t host_upload_size = host_slice_size * info.layers;
if (host_upload_size > UINT32_MAX) {
EXIT("Tiler: invalid D16 host-promotion footprint, guest_slice=0x%016" PRIx64
" host_slice=0x%016" PRIx64 " layers=%u\n",
slice_size, host_slice_size, info.layers);
}
Transfer::ScratchBuffer host_linear(host_upload_size);
std::vector<uint16_t> guest_linear(slice_size / sizeof(uint16_t));
std::vector<BufferImageCopy> regions;
regions.reserve(info.layers);
EXIT_IF(host_upload_size > UINT32_MAX);
auto transfer = MakeDepthTransfer(info.size, info.layers, info.guest_format,
info.bytes_per_element, info.width, info.height, info.pitch,
base_layer, vk::ImageAspectFlagBits::eDepth);
std::vector<uint16_t> guest_linear(info.size / sizeof(uint16_t));
GpuDetile(ctx, reinterpret_cast<const void*>(source.address), guest_linear.data(), info.size,
info.size, transfer.infos);
Transfer::ScratchBuffer host_linear(host_upload_size);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = reinterpret_cast<const uint8_t*>(source.address) + slice_size * layer;
TileConvertTiledToLinearDepth(guest_linear.data(), guest_slice, info.guest_format,
info.width, info.height, info.pitch, slice_size);
auto* host_slice = reinterpret_cast<uint32_t*>(static_cast<uint8_t*>(host_linear.Data()) +
host_slice_size * layer);
const auto* guest = guest_linear.data() + guest_slice_size / sizeof(uint16_t) * layer;
auto* host = reinterpret_cast<uint32_t*>(static_cast<uint8_t*>(host_linear.Data()) +
host_slice_size * layer);
for (uint64_t texel = 0; texel < texels; texel++) {
host_slice[texel] = Encode(guest_linear[texel]);
host[texel] = Encode(guest[texel]);
}
BufferImageCopy region {};
region.offset = static_cast<uint32_t>(host_slice_size * layer);
region.pitch = info.pitch;
region.width = info.width;
region.height = info.height;
region.dst_layer = base_layer + layer;
region.aspect = vk::ImageAspectFlagBits::eDepth;
regions.push_back(region);
transfer.regions[layer].offset = static_cast<uint32_t>(host_slice_size * layer);
}
Transfer::UploadImage(ctx, image, host_linear.Data(), host_upload_size, regions,
Transfer::UploadImage(ctx, image, host_linear.Data(), host_upload_size, transfer.regions,
vk::ImageLayout::eDepthStencilAttachmentOptimal);
}
} // namespace
void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image, const ImageInfo& info,
const BufferImageCopySource& source, bool refresh, bool storage) const {
if (refresh) {
Transfer::WaitForGraphicsIdle(ctx);
}
if (refresh) Transfer::WaitForGraphicsIdle(ctx);
const bool array_texture = TextureIsLayeredTexture(info.type);
const bool volume_texture = TextureIs3DTexture(info.type);
auto layout = TextureCalcUploadLayout(
info.format, info.width, info.height, info.levels, info.depth, info.pitch, info.tile,
info.size, true, false, volume_texture, storage ? "StorageTextureCache" : "TextureCache");
const auto slice_layout = TextureUploadSliceLayout::MipChainPerSlice;
info.size, true, volume_texture, storage ? "StorageTextureCache" : "TextureCache");
auto regions = TextureBuildUploadRegions(layout, image->format, info.width, info.height,
info.depth, info.levels, array_texture, volume_texture,
TextureUploadDestination::MipLevels, slice_layout);
TextureUploadGuestImage(
ctx, image, reinterpret_cast<const void*>(source.address), info.size, regions, layout,
info.format, info.width, info.height, info.depth, info.levels, slice_layout,
storage ? "StorageTextureCache" : "TextureCache",
storage ? vk::ImageLayout::eGeneral : vk::ImageLayout::eShaderReadOnlyOptimal);
TextureUploadDestination::MipLevels);
TextureUploadGuestImage(ctx, image, reinterpret_cast<const void*>(source.address), info.size,
regions, layout, info.format, info.width, info.height, info.depth,
info.levels, storage ? "StorageTextureCache" : "TextureCache",
storage ? vk::ImageLayout::eGeneral
: vk::ImageLayout::eShaderReadOnlyOptimal);
}
void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh, uint32_t base_layer) const {
if (info.samples != 1 || image == nullptr || image->samples != 1) {
EXIT("Tiler: multisampled depth upload is unsupported, samples=%u/%u\n", info.samples,
image != nullptr ? image->samples : 0);
}
if (refresh) {
Transfer::WaitForGraphicsIdle(ctx);
}
EXIT_NOT_IMPLEMENTED(info.samples != 1 || image->samples != 1);
if (refresh) Transfer::WaitForGraphicsIdle(ctx);
if (DepthAspectTransferBytes(info.format) != info.bytes_per_element) {
switch (info.format) {
case vk::Format::eD24UnormS8Uint:
@@ -92,165 +138,25 @@ void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
case vk::Format::eD32SfloatS8Uint:
UploadPromotedD16Depth<EncodeD16AsD32>(ctx, image, info, source, base_layer);
return;
default:
EXIT("Tiler: unsupported depth transfer conversion, format=%d guest_bpe=%u\n",
static_cast<int>(info.format), info.bytes_per_element);
default: EXIT_NOT_IMPLEMENTED(true);
}
}
const auto slice_size = info.size / info.layers;
Transfer::ScratchBuffer linear(info.size);
std::vector<BufferImageCopy> regions;
regions.reserve(info.layers);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* linear_slice = static_cast<uint8_t*>(linear.Data()) + slice_size * layer;
auto* guest_slice = reinterpret_cast<const uint8_t*>(source.address) + slice_size * layer;
TileConvertTiledToLinearDepth(linear_slice, guest_slice, info.guest_format, info.width,
info.height, info.pitch, slice_size);
BufferImageCopy region {};
region.offset = static_cast<uint32_t>(slice_size * layer);
region.pitch = info.pitch;
region.width = info.width;
region.height = info.height;
region.dst_layer = base_layer + layer;
region.aspect = vk::ImageAspectFlagBits::eDepth;
regions.push_back(region);
}
Transfer::UploadImage(ctx, image, linear.Data(), info.size, regions,
vk::ImageLayout::eDepthStencilAttachmentOptimal);
UploadDepth(ctx, image, source.address, info.size, info.layers, info.guest_format,
info.bytes_per_element, info.width, info.height, info.pitch, base_layer,
vk::ImageAspectFlagBits::eDepth);
}
void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh, uint32_t base_layer) const {
if (info.samples != 1 || image == nullptr || image->samples != 1) {
EXIT("Tiler: multisampled stencil upload is unsupported, samples=%u/%u\n", info.samples,
image != nullptr ? image->samples : 0);
}
const auto stencil_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
const auto stencil_pitch = TileGetTexturePitch(
stencil_format, info.width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
if (refresh) {
Transfer::WaitForGraphicsIdle(ctx);
}
const auto slice_size = info.stencil_size / info.layers;
Transfer::ScratchBuffer linear(info.stencil_size);
std::vector<BufferImageCopy> regions;
regions.reserve(info.layers);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* linear_slice = static_cast<uint8_t*>(linear.Data()) + slice_size * layer;
auto* guest_slice = reinterpret_cast<const uint8_t*>(source.address) + slice_size * layer;
TileConvertTiledToLinearDepth(linear_slice, guest_slice, stencil_format, info.width,
info.height, stencil_pitch, slice_size);
BufferImageCopy region {};
region.offset = static_cast<uint32_t>(slice_size * layer);
region.pitch = stencil_pitch;
region.width = info.width;
region.height = info.height;
region.dst_layer = base_layer + layer;
region.aspect = vk::ImageAspectFlagBits::eStencil;
regions.push_back(region);
}
Transfer::UploadImage(ctx, image, linear.Data(), info.stencil_size, regions,
vk::ImageLayout::eDepthStencilAttachmentOptimal);
}
EXIT_NOT_IMPLEMENTED(info.samples != 1 || image->samples != 1);
if (refresh) Transfer::WaitForGraphicsIdle(ctx);
void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info) const {
const bool standard64 = IsSupportedStandard64RenderTarget(info);
if ((info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) &&
!standard64) ||
info.levels != 1 || info.samples != 1) {
EXIT("Tiler: unsupported render-target tile, dst=%p src=%p "
"addr=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%u pitch=%u levels=%u tile=%u bpe=%u\n",
dst, src, info.address, info.size, info.width, info.height, info.pitch, info.levels,
info.tile_mode, info.bytes_per_element);
}
const auto slice_size = info.size / info.layers;
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer;
auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer;
if (standard64) {
TileConvertLinearToTiledStandard64KB32(guest_slice, linear_slice, info.width,
info.height, info.pitch, slice_size);
} else {
TileConvertLinearToTiledRenderTarget(guest_slice, linear_slice, info.width, info.height,
info.pitch, info.bytes_per_element, slice_size);
}
}
}
void Tiler::TileImage(void* dst, const void* src, const ImageInfo& info) const {
const bool image_2d =
info.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) && info.depth == 1;
const auto bytes_per_element = Prospero::RenderTargetBytesPerElement(info.format);
if (!image_2d || info.levels == 0 || info.levels > 16 ||
info.tile != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
bytes_per_element == 0) {
EXIT("Tiler: unsupported storage-texture tile, addr=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%ux%u levels=%u tile=%u format=%u\n",
info.address, info.size, info.width, info.height, info.depth, info.levels, info.tile,
info.format);
}
auto layout = TextureCalcUploadLayout(info.format, info.width, info.height, info.levels,
info.depth, info.pitch, info.tile, info.size, true, false,
false, "StorageTextureReadback");
auto regions = TextureBuildUploadRegions(
layout, VulkanFormat(info.format), info.width, info.height, info.depth, info.levels, false,
false, TextureUploadDestination::MipLevels, TextureUploadSliceLayout::MipChainPerSlice);
std::memset(dst, 0, info.size);
for (uint32_t level = 0; level < info.levels; level++) {
const auto& level_size = layout.level_sizes[level];
const auto guest_offset =
level_size.src_size != 0 ? level_size.src_offset : level_size.offset;
auto* guest = static_cast<uint8_t*>(dst) + guest_offset;
const auto* linear = static_cast<const uint8_t*>(src) + regions[level].offset;
TileConvertLinearToTiledRenderTarget(
guest, linear, regions[level].width, regions[level].height, regions[level].pitch,
bytes_per_element, level_size.size, level_size.src_size, level_size.x, level_size.y);
}
}
void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) const {
if (info.samples != 1 || info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) ||
!IsSupportedDepthReadbackFormat(info)) {
EXIT("Tiler: unsupported depth-target tile, dst=%p src=%p "
"depth=0x%016" PRIx64 "+0x%016" PRIx64 " stencil=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%u pitch=%u tile=%u format=%d guest_format=%u bpe=%u\n",
dst, src, info.address, info.size, info.stencil_address, info.stencil_size, info.width,
info.height, info.pitch, info.tile_mode, static_cast<int>(info.format),
info.guest_format, info.bytes_per_element);
}
const auto slice_size = info.size / info.layers;
std::memset(dst, 0, info.size);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer;
auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer;
TileConvertLinearToTiledDepth(guest_slice, linear_slice, info.guest_format, info.width,
info.height, info.pitch, slice_size);
}
}
void Tiler::TileStencil(void* dst, const void* src, const DepthTargetInfo& info) const {
const auto format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
const auto pitch = TileGetTexturePitch(format, info.width, 1,
Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
if (info.samples != 1 || info.stencil_address == 0 || info.stencil_size == 0 ||
info.layers == 0 || info.stencil_size % info.layers != 0 ||
!IsSupportedDepthReadbackFormat(info)) {
EXIT("Tiler: unsupported stencil-target tile, dst=%p src=%p "
"stencil=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%u pitch=%u layers=%u format=%d compressed=%d\n",
dst, src, info.stencil_address, info.stencil_size, info.width, info.height, pitch,
info.layers, static_cast<int>(info.format), info.stencil_htile_compressed);
}
const auto slice_size = info.stencil_size / info.layers;
std::memset(dst, 0, info.stencil_size);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer;
auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer;
TileConvertLinearToTiledDepth(guest_slice, linear_slice, format, info.width, info.height,
pitch, slice_size);
}
UploadDepth(ctx, image, source.address, info.stencil_size, info.layers, format, 1, info.width,
info.height, pitch, base_layer, vk::ImageAspectFlagBits::eStencil);
}
} // namespace Libs::Graphics
-4
View File
@@ -24,10 +24,6 @@ public:
void DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh, uint32_t base_layer = 0) const;
void TileImage(void* dst, const void* src, const RenderTargetInfo& info) const;
void TileImage(void* dst, const void* src, const ImageInfo& info) const;
void TileImage(void* dst, const void* src, const DepthTargetInfo& info) const;
void TileStencil(void* dst, const void* src, const DepthTargetInfo& info) const;
};
} // namespace Libs::Graphics
@@ -0,0 +1,70 @@
layout(local_size_x = 64) in;
layout(set = 0, binding = 0, std430) readonly buffer Input { uint data[]; } input_buffer;
layout(set = 0, binding = 1, std430) buffer Output { uint data[]; } output_buffer;
layout(push_constant) uniform Push {
uint src_base;
uint dst_base;
uint width;
uint height;
uint depth;
uint surface_z;
uint pitch_bytes;
uint slice_bytes;
uint blocks_per_row;
uint blocks_per_slice;
uint tail_x;
uint tail_y;
uint tail;
uint first;
uint count;
} params;
void copy_element(uint src, uint dst) {
if (ELEMENT_BYTES >= 4) {
for (uint i = 0; i < ELEMENT_BYTES; i += 4) {
output_buffer.data[(dst + i) >> 2] = input_buffer.data[(src + i) >> 2];
}
} else {
uint mask = ELEMENT_BYTES == 1 ? 0xffu : 0xffffu;
uint value = (input_buffer.data[src >> 2] >> ((src & 3u) * 8u)) & mask;
atomicOr(output_buffer.data[dst >> 2], value << ((dst & 3u) * 8u));
}
}
void main() {
uint index = gl_GlobalInvocationID.x;
if (index >= params.count) {
return;
}
uint element = params.first + index;
uint plane = params.width * params.height;
uvec3 p;
p.z = element / plane;
element -= p.z * plane;
p.y = element / params.width;
p.x = element - p.y * params.width;
uvec3 extent = block_extent();
uvec3 swizzle = p;
uvec3 block = uvec3(0);
if (params.tail != 0) {
swizzle.xy += uvec2(params.tail_x, params.tail_y);
} else {
block = p / extent;
}
uint block_index = block.z * params.blocks_per_slice +
block.y * params.blocks_per_row + block.x;
uint tiled = block_index * BLOCK_BYTES +
block_offset(uvec3(swizzle.xy, swizzle.z + params.surface_z));
uint linear = p.z * params.slice_bytes + p.y * params.pitch_bytes +
p.x * ELEMENT_BYTES;
if (TILE == 0) {
copy_element(params.src_base + tiled, params.dst_base + linear);
} else {
copy_element(params.src_base + linear, params.dst_base + tiled);
}
}
@@ -0,0 +1,41 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 65536u;
uvec3 block_extent() {
uint width = ELEMENT_BYTES <= 2 ? 256u : 128u;
return uvec3(width, BLOCK_BYTES / (width * ELEMENT_BYTES), 1u);
}
uint block_offset(uvec3 p) {
uint x = p.x, y = p.y;
uint z = ((p.z & 8u) << 5) ^ ((p.z & 4u) << 7) ^
((p.z & 2u) << 9) ^ ((p.z & 1u) << 11);
switch (ELEMENT_BYTES) {
case 1: return z ^ (x & 1u) ^ ((x << 1) & 0x004u) ^ ((x << 2) & 0x010u) ^
((x << 3) & 0x040u) ^ ((x << 5) & 0x300u) ^
((x << 4) & 0x400u) ^ ((x << 6) & 0x800u) ^
((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^
((y << 1) & 0x002u) ^ ((y << 2) & 0x008u) ^
((y << 3) & 0x0a0u) ^ ((y << 5) & 0xf00u) ^
((y << 6) & 0x1000u) ^ ((y << 7) & 0x4000u);
case 2: return z ^ ((x << 1) & 0x002u) ^ ((x << 2) & 0x008u) ^
((x << 3) & 0x020u) ^ ((x << 4) & 0x480u) ^
((x << 5) & 0x300u) ^ ((x << 6) & 0x800u) ^
((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^
((y << 2) & 0x004u) ^ ((y << 3) & 0x010u) ^
((y << 4) & 0x040u) ^ ((y << 5) & 0xf00u) ^ ((y << 8) & 0x5000u);
case 4: return z ^ ((x << 2) & 0x004u) ^ ((x << 3) & 0x010u) ^
((x << 4) & 0x440u) ^ ((x << 5) & 0x300u) ^
((x << 6) & 0x800u) ^ ((x << 9) & 0xa000u) ^
((y << 3) & 0x008u) ^ ((y << 4) & 0x020u) ^
((y << 5) & 0xf80u) ^ ((y << 9) & 0x1000u) ^
((y << 8) & 0x4000u);
default: return z ^ ((x << 3) & 0x008u) ^ ((x << 4) & 0x420u) ^
((x << 5) & 0x380u) ^ ((x << 6) & 0x800u) ^
((x << 10) & 0x2000u) ^ ((x << 9) & 0x8000u) ^
((y << 4) & 0x010u) ^ ((y << 5) & 0xf40u) ^
((y << 10) & 0x5000u);
}
}
#include "gpu_tiler_common.inc"
@@ -0,0 +1,20 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 65536u;
#include "gpu_tiler_standard64.inc"
uvec3 block_extent() {
uint width = ELEMENT_BYTES <= 2 ? 256u : (ELEMENT_BYTES <= 8 ? 128u : 64u);
return uvec3(width, BLOCK_BYTES / (width * ELEMENT_BYTES), 1u);
}
uint block_offset(uvec3 p) {
uvec4 s = ELEMENT_BYTES == 1 ? uvec4(7, 7, 6, 6) :
ELEMENT_BYTES == 2 ? uvec4(7, 6, 6, 5) :
ELEMENT_BYTES == 4 ? uvec4(6, 6, 5, 5) :
ELEMENT_BYTES == 8 ? uvec4(6, 5, 5, 4) : uvec4(5, 5, 4, 4);
uint delta = (((p.x >> s.x) & 1u) << 8) ^ (((p.y >> s.y) & 1u) << 9) ^
(((p.x >> s.z) & 1u) << 10) ^ (((p.y >> s.w) & 1u) << 11);
return standard64_offset(p.xy) ^ delta;
}
#include "gpu_tiler_common.inc"
@@ -0,0 +1,17 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 65536u;
#include "gpu_tiler_standard64_3d.inc"
uvec3 block_extent() { return standard64_3d_extent(); }
uint block_offset(uvec3 p) {
uvec4 s = ELEMENT_BYTES == 1 ? uvec4(4, 5, 4, 4) :
ELEMENT_BYTES == 2 ? uvec4(4, 4, 3, 4) :
ELEMENT_BYTES == 4 ? uvec4(4, 4, 3, 3) :
ELEMENT_BYTES == 8 ? uvec4(3, 4, 3, 3) : uvec4(3, 3, 2, 3);
uint delta = (((p.y >> s.x) & 1u) << 10) ^ (((p.x >> s.y) & 1u) << 10) ^
(((p.x >> s.z) & 1u) << 11) ^ (((p.z >> s.w) & 1u) << 11);
return standard64_3d_offset(p) ^ delta;
}
#include "gpu_tiler_common.inc"
@@ -0,0 +1,42 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 65536u;
uvec3 block_extent() {
uint width = ELEMENT_BYTES <= 2 ? 256u : (ELEMENT_BYTES <= 8 ? 128u : 64u);
return uvec3(width, BLOCK_BYTES / (width * ELEMENT_BYTES), 1u);
}
uint rt_offset(uint x, uint y) {
switch (ELEMENT_BYTES) {
case 1: return ((y << 2) & 0x008u) ^ ((y << 4) & 0x010u) ^
((y << 3) & 0x0a0u) ^ ((y << 5) & 0xf00u) ^
((y << 6) & 0x1000u) ^ ((y << 7) & 0x4000u) ^ (x & 7u) ^
((x << 3) & 0x040u) ^ ((x << 5) & 0x300u) ^
((x << 4) & 0x400u) ^ ((x << 6) & 0x800u) ^
((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u);
case 2: return ((y << 4) & 0x070u) ^ ((y << 5) & 0xf00u) ^
((y << 8) & 0x5000u) ^ ((x << 1) & 0x00eu) ^
((x << 4) & 0x480u) ^ ((x << 5) & 0x300u) ^
((x << 6) & 0x800u) ^ ((x << 7) & 0x2000u) ^
((x << 8) & 0x8000u);
case 4: return ((y << 4) & 0x070u) ^ ((y << 5) & 0xf00u) ^
((y << 9) & 0x1000u) ^ ((y << 8) & 0x4000u) ^
((x << 2) & 0x00cu) ^ ((x << 5) & 0x380u) ^
((x << 4) & 0x400u) ^ ((x << 6) & 0x800u) ^ ((x << 9) & 0xa000u);
case 8: return ((y << 4) & 0x010u) ^ ((y << 6) & 0x080u) ^
((y << 5) & 0xf00u) ^ ((y << 10) & 0x5000u) ^
((x << 3) & 0x008u) ^ ((x << 4) & 0x460u) ^
((x << 5) & 0x300u) ^ ((x << 6) & 0x800u) ^
((x << 10) & 0x2000u) ^ ((x << 9) & 0x8000u);
default: return ((x << 4) & 0x410u) ^ ((x << 5) & 0x340u) ^
((x << 6) & 0x800u) ^ ((x << 11) & 0xa000u) ^
((y << 5) & 0xf20u) ^ ((y << 6) & 0x080u) ^
((y << 10) & 0x1000u) ^ ((y << 11) & 0x4000u);
}
}
uint volume_z(uint z) {
return ((z & 8u) << 5) ^ ((z & 4u) << 7) ^ ((z & 2u) << 9) ^ ((z & 1u) << 11);
}
uint block_offset(uvec3 p) { return rt_offset(p.x, p.y) ^ volume_z(p.z); }
#include "gpu_tiler_common.inc"
@@ -0,0 +1,18 @@
uint standard_offset(uvec2 p) {
uint x = p.x, y = p.y;
switch (ELEMENT_BYTES) {
case 1: return ((y << 4) & 0x1f0u) ^ ((y << 5) & 0x400u) ^ (x & 0x00fu) ^
((x << 5) & 0x200u) ^ ((x << 6) & 0x800u);
case 2: return ((y << 4) & 0x070u) ^ ((y << 5) & 0x100u) ^ ((y << 6) & 0x400u) ^
((x << 1) & 0x00eu) ^ ((x << 4) & 0x080u) ^
((x << 5) & 0x200u) ^ ((x << 6) & 0x800u);
case 4: return ((y << 4) & 0x070u) ^ ((y << 5) & 0x100u) ^ ((y << 6) & 0x400u) ^
((x << 2) & 0x00cu) ^ ((x << 5) & 0x080u) ^
((x << 6) & 0x200u) ^ ((x << 7) & 0x800u);
case 8: return ((y << 4) & 0x030u) ^ ((y << 6) & 0x100u) ^ ((y << 7) & 0x400u) ^
((x << 3) & 0x008u) ^ ((x << 5) & 0x0c0u) ^
((x << 6) & 0x200u) ^ ((x << 7) & 0x800u);
default: return ((y << 4) & 0x030u) ^ ((y << 6) & 0x100u) ^ ((y << 7) & 0x400u) ^
((x << 6) & 0x0c0u) ^ ((x << 7) & 0x200u) ^ ((x << 8) & 0x800u);
}
}
@@ -0,0 +1,12 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 256u;
#include "gpu_tiler_standard.inc"
uvec3 block_extent() {
uint width = ELEMENT_BYTES <= 2 ? 16u : (ELEMENT_BYTES <= 8 ? 8u : 4u);
return uvec3(width, BLOCK_BYTES / (width * ELEMENT_BYTES), 1u);
}
uint block_offset(uvec3 p) { return standard_offset(p.xy) & 0xffu; }
#include "gpu_tiler_common.inc"
@@ -0,0 +1,12 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 4096u;
#include "gpu_tiler_standard.inc"
uvec3 block_extent() {
uint width = ELEMENT_BYTES <= 2 ? 64u : (ELEMENT_BYTES <= 8 ? 32u : 16u);
return uvec3(width, BLOCK_BYTES / (width * ELEMENT_BYTES), 1u);
}
uint block_offset(uvec3 p) { return standard_offset(p.xy); }
#include "gpu_tiler_common.inc"
@@ -0,0 +1,15 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 4096u;
#include "gpu_tiler_standard4_3d.inc"
uvec3 block_extent() {
if (ELEMENT_BYTES == 1) return uvec3(16, 16, 16);
if (ELEMENT_BYTES == 2) return uvec3(8, 16, 16);
if (ELEMENT_BYTES == 4) return uvec3(8, 16, 8);
if (ELEMENT_BYTES == 8) return uvec3(8, 8, 8);
return uvec3(4, 8, 8);
}
uint block_offset(uvec3 p) { return standard3d_offset(p); }
#include "gpu_tiler_common.inc"
@@ -0,0 +1,22 @@
uint standard3d_offset(uvec3 p) {
uint x = p.x, y = p.y, z = p.z;
if (ELEMENT_BYTES == 1)
return (x & 3u) ^ ((x << 4) & 0x40u) ^ ((x << 6) & 0x200u) ^
((y << 3) & 8u) ^ ((y << 4) & 0x20u) ^ ((y << 6) & 0x100u) ^
((y << 8) & 0x800u) ^ ((z << 2) & 4u) ^ ((z << 3) & 0x10u) ^
((z << 5) & 0x80u) ^ ((z << 7) & 0x400u);
if (ELEMENT_BYTES == 2)
return ((x << 1) & 2u) ^ ((x << 5) & 0x40u) ^ ((x << 7) & 0x200u) ^
((y << 3) & 8u) ^ ((y << 4) & 0x20u) ^ ((y << 6) & 0x100u) ^
((y << 8) & 0x800u) ^ ((z << 2) & 4u) ^ ((z << 3) & 0x10u) ^
((z << 5) & 0x80u) ^ ((z << 7) & 0x400u);
uint z_part = ((z << 4) & 0x10u) ^ ((z << 6) & 0x80u) ^ ((z << 8) & 0x400u);
if (ELEMENT_BYTES == 4)
return ((x << 2) & 4u) ^ ((x << 5) & 0x40u) ^ ((x << 7) & 0x200u) ^
((y << 3) & 8u) ^ ((y << 4) & 0x20u) ^ ((y << 6) & 0x100u) ^
((y << 8) & 0x800u) ^ z_part;
uint y_part = ((y << 5) & 0x20u) ^ ((y << 7) & 0x100u) ^ ((y << 9) & 0x800u);
if (ELEMENT_BYTES == 8)
return ((x << 3) & 8u) ^ ((x << 5) & 0x40u) ^ ((x << 7) & 0x200u) ^ y_part ^ z_part;
return ((x << 6) & 0x40u) ^ ((x << 8) & 0x200u) ^ y_part ^ z_part;
}
@@ -0,0 +1,12 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 65536u;
#include "gpu_tiler_standard64.inc"
uvec3 block_extent() {
uint width = ELEMENT_BYTES <= 2 ? 256u : (ELEMENT_BYTES <= 8 ? 128u : 64u);
return uvec3(width, BLOCK_BYTES / (width * ELEMENT_BYTES), 1u);
}
uint block_offset(uvec3 p) { return standard64_offset(p.xy); }
#include "gpu_tiler_common.inc"
@@ -0,0 +1,29 @@
uint standard64_offset(uvec2 p) {
uint x = p.x, y = p.y;
switch (ELEMENT_BYTES) {
case 1: return (x & 0x0fu) ^ ((x << 5) & 0x200u) ^ ((x << 6) & 0x800u) ^
((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^
((y << 4) & 0x1f0u) ^ ((y << 5) & 0x400u) ^
((y << 6) & 0x1000u) ^ ((y << 7) & 0x4000u);
case 2: return ((x << 1) & 0x00eu) ^ ((x << 4) & 0x080u) ^
((x << 5) & 0x200u) ^ ((x << 6) & 0x800u) ^
((x << 7) & 0x2000u) ^ ((x << 8) & 0x8000u) ^
((y << 4) & 0x070u) ^ ((y << 5) & 0x100u) ^
((y << 6) & 0x400u) ^ ((y << 7) & 0x1000u) ^ ((y << 8) & 0x4000u);
case 4: return ((x << 2) & 0x00cu) ^ ((x << 5) & 0x080u) ^
((x << 6) & 0x200u) ^ ((x << 7) & 0x800u) ^
((x << 8) & 0x2000u) ^ ((x << 9) & 0x8000u) ^
((y << 4) & 0x070u) ^ ((y << 5) & 0x100u) ^
((y << 6) & 0x400u) ^ ((y << 7) & 0x1000u) ^ ((y << 8) & 0x4000u);
case 8: return ((x << 3) & 0x008u) ^ ((x << 5) & 0x0c0u) ^
((x << 6) & 0x200u) ^ ((x << 7) & 0x800u) ^
((x << 8) & 0x2000u) ^ ((x << 9) & 0x8000u) ^
((y << 4) & 0x030u) ^ ((y << 6) & 0x100u) ^
((y << 7) & 0x400u) ^ ((y << 8) & 0x1000u) ^ ((y << 9) & 0x4000u);
default: return ((x << 6) & 0x0c0u) ^ ((x << 7) & 0x200u) ^
((x << 8) & 0x800u) ^ ((x << 9) & 0x2000u) ^
((x << 10) & 0x8000u) ^ ((y << 4) & 0x030u) ^
((y << 6) & 0x100u) ^ ((y << 7) & 0x400u) ^
((y << 8) & 0x1000u) ^ ((y << 9) & 0x4000u);
}
}
@@ -0,0 +1,9 @@
#version 450
#extension GL_GOOGLE_include_directive : require
layout(constant_id = 0) const uint ELEMENT_BYTES = 4u;
layout(constant_id = 1) const uint TILE = 0u;
const uint BLOCK_BYTES = 65536u;
#include "gpu_tiler_standard64_3d.inc"
uvec3 block_extent() { return standard64_3d_extent(); }
uint block_offset(uvec3 p) { return standard64_3d_offset(p); }
#include "gpu_tiler_common.inc"
@@ -0,0 +1,17 @@
#include "gpu_tiler_standard4_3d.inc"
uvec3 standard64_3d_extent() {
if (ELEMENT_BYTES == 1) return uvec3(64, 32, 32);
if (ELEMENT_BYTES == 2) return uvec3(32, 32, 32);
if (ELEMENT_BYTES == 4) return uvec3(32, 32, 16);
if (ELEMENT_BYTES == 8) return uvec3(32, 16, 16);
return uvec3(16, 16, 16);
}
uint standard64_3d_offset(uvec3 p) {
uvec4 s = ELEMENT_BYTES == 1 ? uvec4(4, 4, 4, 5) :
ELEMENT_BYTES == 2 ? uvec4(3, 4, 4, 4) :
ELEMENT_BYTES == 4 ? uvec4(3, 3, 4, 4) :
ELEMENT_BYTES == 8 ? uvec4(3, 3, 3, 4) : uvec4(2, 3, 3, 3);
return standard3d_offset(p) ^ (((p.x >> s.x) & 1u) << 12) ^
(((p.z >> s.y) & 1u) << 13) ^ (((p.y >> s.z) & 1u) << 14) ^
(((p.x >> s.w) & 1u) << 15);
}
+49 -18
View File
@@ -4,6 +4,7 @@
#include "common/logging/log.h"
#include "common/profiler.h"
#include "common/threads.h"
#include "graphics/host_gpu/gpuTiler.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/imageView.h"
#include "graphics/host_gpu/renderer/render.h"
@@ -240,9 +241,11 @@ ConvertImageBufferCopies(std::span<const ImageBufferCopy> regions) {
return vk_regions;
}
static void RecordImageToBuffer(CommandBuffer& command, VulkanImage& src_image,
VulkanBuffer& dst_buffer, std::span<const ImageBufferCopy> regions,
vk::ImageLayout final_layout) {
static void
RecordImageToBuffer(CommandBuffer& command, VulkanImage& src_image, VulkanBuffer& dst_buffer,
std::span<const ImageBufferCopy> regions, vk::ImageLayout final_layout,
vk::AccessFlags final_access = vk::AccessFlagBits::eHostRead,
vk::PipelineStageFlags final_stage = vk::PipelineStageFlagBits::eHost) {
auto vk_command = command.Handle();
vk::ImageAspectFlags aspects = {};
for (const auto& region: regions) {
@@ -259,8 +262,8 @@ static void RecordImageToBuffer(CommandBuffer& command, VulkanImage& src_image,
dst_buffer.buffer, static_cast<uint32_t>(copies.size()),
copies.data());
SetBufferMemoryBarrier(vk_command, dst_buffer.buffer, 0, VK_WHOLE_SIZE,
vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eHostRead,
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eHost);
vk::AccessFlagBits::eTransferWrite, final_access,
vk::PipelineStageFlagBits::eTransfer, final_stage);
SetImageLayout(vk_command, &src_image, 0, VK_REMAINING_MIP_LEVELS, aspects,
vk::ImageLayout::eTransferSrcOptimal, final_layout);
}
@@ -358,8 +361,8 @@ public:
}
void ProcessDownloadedImage(GraphicContext* ctx, uint64_t size,
std::span<const ImageBufferCopy> regions,
VulkanImage* src_image, vk::ImageLayout src_layout,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
vk::ImageLayout src_layout,
const DownloadedImageConsumer& consumer) {
WithDownloadedImage(ctx, size, regions, src_image, src_layout,
[&](std::span<const uint8_t> data) {
@@ -367,8 +370,8 @@ public:
consumer(data);
return;
}
// Scattered CPU tiler reads can be much slower from uncached mapped
// memory. Preserve the original sequential-copy path as a fallback.
// Read uncached mappings sequentially before CPU consumers inspect
// them.
m_cached_readback.resize(data.size());
std::memcpy(m_cached_readback.data(), data.data(), data.size());
consumer(m_cached_readback);
@@ -382,17 +385,16 @@ public:
VulkanUnmapMemory(ctx, &m_buffer.memory);
VulkanDeleteBuffer(ctx, &m_buffer);
}
m_capacity = 0;
m_mapped_data = nullptr;
m_host_cached = false;
m_capacity = 0;
m_mapped_data = nullptr;
m_host_cached = false;
}
private:
template <typename Consumer>
void WithDownloadedImage(GraphicContext* ctx, uint64_t size,
std::span<const ImageBufferCopy> regions,
VulkanImage* src_image, vk::ImageLayout src_layout,
const Consumer& consumer) {
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
vk::ImageLayout src_layout, const Consumer& consumer) {
Common::LockGuard lock(m_mutex);
EnsureBuffer(ctx, size, vk::BufferUsageFlagBits::eTransferDst);
ExecuteImmediateCommands([&](CommandBuffer* command, vk::CommandBuffer) {
@@ -438,7 +440,7 @@ private:
? vk::MemoryPropertyFlags(vk::MemoryPropertyFlagBits::eHostCached)
: vk::MemoryPropertyFlags {};
VulkanCreateBuffer(ctx, size, &m_buffer);
m_capacity = size;
m_capacity = size;
const auto& properties = ctx->GetPhysicalDeviceMemoryProperties();
EXIT_IF(m_buffer.memory.type >= properties.memoryTypeCount);
m_host_cached =
@@ -518,11 +520,41 @@ static ReusableStagingBuffer* GetStagingBuffer(StagingBufferType type) {
void ReleaseCachedResources(GraphicContext* ctx) {
EXIT_IF(ctx == nullptr);
GpuTileRelease(ctx);
g_texture_staging_buffer.Release(ctx);
g_vertex_staging_buffer.Release(ctx);
g_readback_staging_buffer.Release(ctx);
}
void UploadTiledImage(GraphicContext* ctx, VulkanImage* dst_image, const void* tiled_data,
uint64_t tiled_size, uint64_t linear_size, std::span<const GpuTileInfo> infos,
std::span<const BufferImageCopy> regions, vk::ImageLayout dst_layout) {
EXIT_IF(ctx == nullptr || dst_image == nullptr || tiled_data == nullptr || regions.empty());
GpuDetile(ctx, tiled_data, nullptr, tiled_size, linear_size, infos,
[&](CommandBuffer* command, VulkanBuffer* linear) {
vk::ImageAspectFlags aspects {};
for (const auto& region: regions) {
aspects |= GetTransferAspects(*dst_image, region.aspect);
}
RecordBufferToImageCopy(*command, *linear, *dst_image,
ConvertBufferImageCopies(regions), aspects,
dst_image->layout, dst_layout);
});
}
void DownloadTiledImage(GraphicContext* ctx, void* tiled_data, uint64_t tiled_size,
uint64_t linear_size, std::span<const GpuTileInfo> infos,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
vk::ImageLayout src_layout) {
EXIT_IF(ctx == nullptr || tiled_data == nullptr || src_image == nullptr || regions.empty());
GpuTile(ctx, nullptr, tiled_data, tiled_size, linear_size, infos,
[&](CommandBuffer* command, VulkanBuffer* linear) {
RecordImageToBuffer(*command, *src_image, *linear, regions, src_layout,
vk::AccessFlagBits::eShaderRead,
vk::PipelineStageFlagBits::eComputeShader);
});
}
static void SetImageLayout(vk::CommandBuffer buffer, VulkanImage* dst_image, uint32_t base_level,
uint32_t levels, vk::ImageAspectFlags aspect_mask,
vk::ImageLayout old_image_layout, vk::ImageLayout new_image_layout) {
@@ -973,8 +1005,7 @@ void DownloadImage(GraphicContext* ctx, void* dst_data, uint64_t size,
void ProcessDownloadedImage(GraphicContext* ctx, uint64_t size,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
vk::ImageLayout src_layout,
const DownloadedImageConsumer& consumer) {
vk::ImageLayout src_layout, const DownloadedImageConsumer& consumer) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(size == 0 || regions.empty() || !consumer);
GetStagingBuffer(StagingBufferType::ReadBack)
+9 -2
View File
@@ -3,6 +3,7 @@
#include "common/abi.h"
#include "common/common.h"
#include "graphics/host_gpu/gpuTiler.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <functional>
@@ -102,6 +103,9 @@ void UploadImage(GraphicContext* ctx, DepthStencilVulkanImage* dst_image, const
uint64_t size, uint32_t src_pitch, vk::ImageAspectFlags aspect);
void UploadImage(GraphicContext* ctx, VulkanImage* dst_image, const void* src_data, uint64_t size,
std::span<const BufferImageCopy> regions, vk::ImageLayout dst_layout);
void UploadTiledImage(GraphicContext* ctx, VulkanImage* dst_image, const void* tiled_data,
uint64_t tiled_size, uint64_t linear_size, std::span<const GpuTileInfo> infos,
std::span<const BufferImageCopy> regions, vk::ImageLayout dst_layout);
void CopyImageImmediate(GraphicContext* ctx, std::span<const ImageImageCopy> regions,
VulkanImage* dst_image, vk::ImageLayout dst_layout);
void DownloadImage(GraphicContext* ctx, void* dst_data, uint64_t size, uint32_t dst_pitch,
@@ -115,8 +119,11 @@ void DownloadImage(GraphicContext* ctx, void* dst_data, uint64_t size,
// re-enter Transfer.
void ProcessDownloadedImage(GraphicContext* ctx, uint64_t size,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
vk::ImageLayout src_layout,
const DownloadedImageConsumer& consumer);
vk::ImageLayout src_layout, const DownloadedImageConsumer& consumer);
void DownloadTiledImage(GraphicContext* ctx, void* tiled_data, uint64_t tiled_size,
uint64_t linear_size, std::span<const GpuTileInfo> infos,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
vk::ImageLayout src_layout);
void UploadBuffer(GraphicContext* ctx, StagingBufferType type, VulkanBuffer* dst_buffer,
uint64_t dst_offset, const void* src_data, uint64_t size);
void CopyBuffer(VulkanBuffer* src_buffer, VulkanBuffer* dst_buffer, uint64_t size);
-1
View File
@@ -52,7 +52,6 @@ KYTY_SUBSYSTEM_INIT(Graphics) {
GraphicsRenderInit();
GraphicsRunInit();
LabelInit();
TileInit();
ShaderInit();
}
File diff suppressed because it is too large Load Diff