mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
Add GPU tilers/tests
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
+502
-1933
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
||||
@@ -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, ©);
|
||||
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, ©);
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -52,7 +52,6 @@ KYTY_SUBSYSTEM_INIT(Graphics) {
|
||||
GraphicsRenderInit();
|
||||
GraphicsRunInit();
|
||||
LabelInit();
|
||||
TileInit();
|
||||
ShaderInit();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user