mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
renderer: replace generation retries with staged resource binding
This commit is contained in:
@@ -636,24 +636,16 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
|||||||
const auto end = AlignUp(vaddr + size);
|
const auto end = AlignUp(vaddr + size);
|
||||||
std::lock_guard transaction(m_resource_mutex);
|
std::lock_guard transaction(m_resource_mutex);
|
||||||
const auto texture_region = m_texture_cache->QueryRegion(vaddr, size);
|
const auto texture_region = m_texture_cache->QueryRegion(vaddr, size);
|
||||||
// Use the stream-buffer fast path before image/buffer alias handling. Clean image and metadata
|
// Snapshot small CPU-current reads now and upload them only during the final non-resetting
|
||||||
// views may coexist with a small CPU-current read; Kyty's separate image trackers require
|
// commit. This keeps unaligned guest addresses valid without tying preparation to a command-local
|
||||||
// GPU-dirty ownership guards here. Read physical backing so
|
// stream allocation.
|
||||||
// host page protection is irrelevant.
|
|
||||||
if (is_read && !is_written && size <= CACHING_PAGE_SIZE &&
|
if (is_read && !is_written && size <= CACHING_PAGE_SIZE &&
|
||||||
!m_memory_tracker.IsRegionGpuModified(vaddr, size) &&
|
!m_memory_tracker.IsRegionGpuModified(vaddr, size) &&
|
||||||
m_memory_tracker.IsRegionCpuModified(vaddr, size) && !texture_region.gpu_image_bytes &&
|
m_memory_tracker.IsRegionCpuModified(vaddr, size) && !texture_region.gpu_image_bytes &&
|
||||||
!texture_region.gpu_metadata_bytes) {
|
!texture_region.gpu_metadata_bytes) {
|
||||||
std::array<uint8_t, CACHING_PAGE_SIZE> guest_data;
|
std::vector<uint8_t> guest_data(size);
|
||||||
if (Libs::LibKernel::Memory::TryReadBacking(vaddr, guest_data.data(), size)) {
|
if (Libs::LibKernel::Memory::TryReadBacking(vaddr, guest_data.data(), size)) {
|
||||||
VulkanBuffer* stream_buffer = nullptr;
|
return {nullptr, 0, std::move(guest_data)};
|
||||||
vk::DeviceSize stream_offset = 0;
|
|
||||||
vk::DeviceSize stream_range = 0;
|
|
||||||
const auto stream_alignment = std::max<uint64_t>(m_graphics.StorageMinAlignment(), 16);
|
|
||||||
if (UploadHostData(command, guest_data.data(), size, stream_alignment, stream_buffer,
|
|
||||||
stream_offset, stream_range)) {
|
|
||||||
return {*stream_buffer, stream_offset};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto texture_pages = begin == vaddr && end - begin == size
|
const auto texture_pages = begin == vaddr && end - begin == size
|
||||||
@@ -829,8 +821,7 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr,
|
|||||||
if (is_written) {
|
if (is_written) {
|
||||||
m_gpu_modified_ranges.Add(vaddr, size);
|
m_gpu_modified_ranges.Add(vaddr, size);
|
||||||
}
|
}
|
||||||
command.RetainResourceUntilFence(cached.buffer);
|
return {cached.buffer, vaddr - cached.vaddr};
|
||||||
return {*cached.buffer, vaddr - cached.vaddr};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferCache::UploadHostData(CommandBuffer& command, const void* src, uint64_t size,
|
bool BufferCache::UploadHostData(CommandBuffer& command, const void* src, uint64_t size,
|
||||||
@@ -842,10 +833,7 @@ bool BufferCache::UploadHostData(CommandBuffer& command, const void* src, uint64
|
|||||||
return command.m_host_stream.Copy(src, size, alignment, out_buffer, out_offset, out_range);
|
return command.m_host_stream.Copy(src, size, alignment, out_buffer, out_offset, out_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanBuffer& BufferCache::ObtainNullBuffer(CommandBuffer& command) {
|
std::shared_ptr<VulkanBuffer> BufferCache::ObtainNullBuffer() {
|
||||||
if (command.IsInvalid() || command.IsExecute()) {
|
|
||||||
EXIT("BufferCache: null buffer requires a graphics context\n");
|
|
||||||
}
|
|
||||||
FaultSafeCacheLock lock(this, m_mutex);
|
FaultSafeCacheLock lock(this, m_mutex);
|
||||||
if (m_null_buffer == nullptr) {
|
if (m_null_buffer == nullptr) {
|
||||||
// robustBufferAccess makes every fetch safe; TODO: Use a
|
// robustBufferAccess makes every fetch safe; TODO: Use a
|
||||||
@@ -865,10 +853,7 @@ VulkanBuffer& BufferCache::ObtainNullBuffer(CommandBuffer& command) {
|
|||||||
m_null_buffer = std::move(buffer);
|
m_null_buffer = std::move(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The buffer remains cache-persistent, while every recorded consumer keeps the allocation
|
return m_null_buffer;
|
||||||
// alive until its own fence even if an unrelated command processor resets the global cache.
|
|
||||||
command.RetainResourceUntilFence(m_null_buffer);
|
|
||||||
return *m_null_buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
|
BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
|
||||||
@@ -1046,17 +1031,20 @@ void BufferCache::FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t si
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXIT_IF(command == nullptr);
|
EXIT_IF(command == nullptr);
|
||||||
const auto [dst, dst_offset] = ObtainBuffer(*command, vaddr, size, true, false);
|
auto dst = ObtainBuffer(*command, vaddr, size, true, false);
|
||||||
const auto before = MakeDmaBarrier(
|
EXIT_IF(dst.buffer == nullptr || !dst.host_data.empty());
|
||||||
dst, dst_offset, size, vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
command->RetainResourceUntilFence(dst.buffer);
|
||||||
|
const auto before = MakeDmaBarrier(
|
||||||
|
*dst.buffer, dst.offset, size,
|
||||||
|
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlagBits::eTransferWrite);
|
vk::AccessFlagBits::eTransferWrite);
|
||||||
const auto vk_buffer = command->Handle();
|
const auto vk_buffer = command->Handle();
|
||||||
vk_buffer.pipelineBarrier(
|
vk_buffer.pipelineBarrier(
|
||||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer,
|
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 1, &before, 0, nullptr);
|
vk::DependencyFlagBits::eByRegion, 0, nullptr, 1, &before, 0, nullptr);
|
||||||
vk_buffer.fillBuffer(dst.buffer, dst_offset, size, value);
|
vk_buffer.fillBuffer(dst.buffer->buffer, dst.offset, size, value);
|
||||||
const auto after =
|
const auto after =
|
||||||
MakeDmaBarrier(dst, dst_offset, size, vk::AccessFlagBits::eTransferWrite,
|
MakeDmaBarrier(*dst.buffer, dst.offset, size, vk::AccessFlagBits::eTransferWrite,
|
||||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite);
|
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite);
|
||||||
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::PipelineStageFlagBits::eAllCommands,
|
vk::PipelineStageFlagBits::eAllCommands,
|
||||||
@@ -1128,31 +1116,44 @@ void BufferCache::CopyBuffer(CommandBuffer* command, uint64_t dst_vaddr, uint64_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXIT_IF(command == nullptr);
|
EXIT_IF(command == nullptr);
|
||||||
const auto [src, src_offset] = ObtainBuffer(*command, src_vaddr, size, false, true);
|
auto src = ObtainBuffer(*command, src_vaddr, size, false, true);
|
||||||
const auto [dst, dst_offset] =
|
auto dst = ObtainBuffer(*command, dst_vaddr, size, true, false, dst_image_transition);
|
||||||
ObtainBuffer(*command, dst_vaddr, size, true, false, dst_image_transition);
|
EXIT_IF(dst.buffer == nullptr || !dst.host_data.empty());
|
||||||
if (&src == &dst && src_offset < dst_offset + size && dst_offset < src_offset + size) {
|
VulkanBuffer* src_buffer = src.buffer.get();
|
||||||
|
vk::DeviceSize src_offset = src.offset;
|
||||||
|
if (!src.host_data.empty()) {
|
||||||
|
vk::DeviceSize range = 0;
|
||||||
|
EXIT_IF(!UploadHostData(*command, src.host_data.data(), src.host_data.size(), 16,
|
||||||
|
src_buffer, src_offset, range) ||
|
||||||
|
range != size);
|
||||||
|
} else {
|
||||||
|
command->RetainResourceUntilFence(src.buffer);
|
||||||
|
}
|
||||||
|
command->RetainResourceUntilFence(dst.buffer);
|
||||||
|
EXIT_IF(src_buffer == nullptr);
|
||||||
|
if (src.buffer == dst.buffer && src.offset < dst.offset + size &&
|
||||||
|
dst.offset < src.offset + size) {
|
||||||
EXIT("BufferCache: resolved Vulkan copy ranges overlap, src_offset=0x%016" PRIx64
|
EXIT("BufferCache: resolved Vulkan copy ranges overlap, src_offset=0x%016" PRIx64
|
||||||
" dst_offset=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
" dst_offset=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
|
||||||
src_offset, dst_offset, size);
|
src.offset, dst.offset, size);
|
||||||
}
|
}
|
||||||
const vk::BufferMemoryBarrier before[] = {
|
const vk::BufferMemoryBarrier before[] = {
|
||||||
MakeDmaBarrier(dst, dst_offset, size,
|
MakeDmaBarrier(*dst.buffer, dst.offset, size,
|
||||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlagBits::eTransferWrite),
|
vk::AccessFlagBits::eTransferWrite),
|
||||||
MakeDmaBarrier(src, src_offset, size, vk::AccessFlagBits::eMemoryWrite,
|
MakeDmaBarrier(*src_buffer, src_offset, size, vk::AccessFlagBits::eMemoryWrite,
|
||||||
vk::AccessFlagBits::eTransferRead),
|
vk::AccessFlagBits::eTransferRead),
|
||||||
};
|
};
|
||||||
const auto vk_buffer = command->Handle();
|
const auto vk_buffer = command->Handle();
|
||||||
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
||||||
vk::PipelineStageFlagBits::eTransfer,
|
vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 2, before, 0, nullptr);
|
vk::DependencyFlagBits::eByRegion, 0, nullptr, 2, before, 0, nullptr);
|
||||||
const vk::BufferCopy copy {src_offset, dst_offset, size};
|
const vk::BufferCopy copy {src_offset, dst.offset, size};
|
||||||
vk_buffer.copyBuffer(src.buffer, dst.buffer, 1, ©);
|
vk_buffer.copyBuffer(src_buffer->buffer, dst.buffer->buffer, 1, ©);
|
||||||
const vk::BufferMemoryBarrier after[] = {
|
const vk::BufferMemoryBarrier after[] = {
|
||||||
MakeDmaBarrier(dst, dst_offset, size, vk::AccessFlagBits::eTransferWrite,
|
MakeDmaBarrier(*dst.buffer, dst.offset, size, vk::AccessFlagBits::eTransferWrite,
|
||||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite),
|
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite),
|
||||||
MakeDmaBarrier(src, src_offset, size, vk::AccessFlagBits::eTransferRead,
|
MakeDmaBarrier(*src_buffer, src_offset, size, vk::AccessFlagBits::eTransferRead,
|
||||||
vk::AccessFlagBits::eMemoryWrite),
|
vk::AccessFlagBits::eMemoryWrite),
|
||||||
};
|
};
|
||||||
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
@@ -36,8 +37,9 @@ struct BufferCacheRange {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BufferBinding {
|
struct BufferBinding {
|
||||||
VulkanBuffer& buffer;
|
std::shared_ptr<VulkanBuffer> buffer;
|
||||||
uint64_t offset;
|
uint64_t offset = 0;
|
||||||
|
std::vector<uint8_t> host_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged,
|
[[nodiscard]] bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged,
|
||||||
@@ -65,7 +67,7 @@ public:
|
|||||||
[[nodiscard]] bool UploadHostData(CommandBuffer& command, const void* src, uint64_t size,
|
[[nodiscard]] bool UploadHostData(CommandBuffer& command, const void* src, uint64_t size,
|
||||||
uint64_t alignment, VulkanBuffer*& out_buffer,
|
uint64_t alignment, VulkanBuffer*& out_buffer,
|
||||||
uint64_t& out_offset, uint64_t& out_range);
|
uint64_t& out_offset, uint64_t& out_range);
|
||||||
[[nodiscard]] VulkanBuffer& ObtainNullBuffer(CommandBuffer& command);
|
[[nodiscard]] std::shared_ptr<VulkanBuffer> ObtainNullBuffer();
|
||||||
[[nodiscard]] BufferImageCopySource ObtainBufferForImage(uint64_t vaddr, uint64_t size);
|
[[nodiscard]] BufferImageCopySource ObtainBufferForImage(uint64_t vaddr, uint64_t size);
|
||||||
void FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t size, uint32_t value);
|
void FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t size, uint32_t value);
|
||||||
void CopyBuffer(CommandBuffer* command, uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size);
|
void CopyBuffer(CommandBuffer* command, uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t size);
|
||||||
|
|||||||
@@ -358,7 +358,6 @@ void CommandBuffer::FinalizeFence(bool reset_recording) {
|
|||||||
m_fence_waited = false;
|
m_fence_waited = false;
|
||||||
if (reset_recording) {
|
if (reset_recording) {
|
||||||
ResetNativeCommandBuffer(m_slot->buffer);
|
ResetNativeCommandBuffer(m_slot->buffer);
|
||||||
m_recording_generation++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (reset_recording) {
|
if (reset_recording) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include "graphics/shader/shaderBindings.h"
|
#include "graphics/shader/shaderBindings.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
namespace ShaderRecompiler::IR {
|
namespace ShaderRecompiler::IR {
|
||||||
struct Program;
|
struct Program;
|
||||||
|
struct ResourceSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommandBuffer;
|
class CommandBuffer;
|
||||||
@@ -29,9 +32,11 @@ struct VulkanDescriptorSet {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BufferView {
|
struct BufferView {
|
||||||
VulkanBuffer* buffer = nullptr;
|
std::shared_ptr<VulkanBuffer> owner;
|
||||||
vk::DeviceSize offset = 0;
|
VulkanBuffer* buffer = nullptr;
|
||||||
vk::DeviceSize range = VK_WHOLE_SIZE;
|
vk::DeviceSize offset = 0;
|
||||||
|
vk::DeviceSize range = VK_WHOLE_SIZE;
|
||||||
|
std::vector<uint8_t> host_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DescriptorCache {
|
class DescriptorCache {
|
||||||
@@ -42,6 +47,7 @@ public:
|
|||||||
VulkanImage* image = nullptr;
|
VulkanImage* image = nullptr;
|
||||||
int view = VulkanImage::VIEW_DEFAULT;
|
int view = VulkanImage::VIEW_DEFAULT;
|
||||||
vk::ImageView image_view = nullptr;
|
vk::ImageView image_view = nullptr;
|
||||||
|
std::shared_ptr<void> owner;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TextureVariant : int {
|
enum class TextureVariant : int {
|
||||||
@@ -63,6 +69,17 @@ public:
|
|||||||
BufferView user_data;
|
BufferView user_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PreparedBindings {
|
||||||
|
std::shared_ptr<const ShaderRecompiler::IR::Program> program;
|
||||||
|
std::shared_ptr<const ShaderRecompiler::IR::ResourceSnapshot> snapshot;
|
||||||
|
NativeDescriptors resources;
|
||||||
|
std::vector<uint32_t> flattened_srt;
|
||||||
|
std::vector<uint32_t> user_data;
|
||||||
|
vk::ShaderStageFlags shader_stage;
|
||||||
|
Stage stage = Stage::Unknown;
|
||||||
|
bool committed = false;
|
||||||
|
};
|
||||||
|
|
||||||
explicit DescriptorCache(GraphicContext& graphics): m_graphics(graphics) {
|
explicit DescriptorCache(GraphicContext& graphics): m_graphics(graphics) {
|
||||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||||
}
|
}
|
||||||
@@ -95,10 +112,14 @@ private:
|
|||||||
std::map<std::vector<uint32_t>, vk::DescriptorSetLayout> m_descriptor_set_layouts;
|
std::map<std::vector<uint32_t>, vk::DescriptorSetLayout> m_descriptor_set_layouts;
|
||||||
};
|
};
|
||||||
|
|
||||||
void BindDescriptors(uint64_t submit_id, CommandBuffer& buffer,
|
[[nodiscard]] DescriptorCache::PreparedBindings
|
||||||
vk::PipelineBindPoint pipeline_bind_point, vk::PipelineLayout layout,
|
PrepareBindings(CommandBuffer& buffer, const ShaderStageRuntime& runtime,
|
||||||
const ShaderStageRuntime& runtime, vk::ShaderStageFlags vk_stage,
|
vk::ShaderStageFlags shader_stage, DescriptorCache::Stage stage);
|
||||||
DescriptorCache::Stage stage);
|
void RebindBuffers(CommandBuffer& buffer, DescriptorCache::PreparedBindings& bindings);
|
||||||
|
void RebindImages(CommandBuffer& buffer, DescriptorCache::PreparedBindings& bindings);
|
||||||
|
void ActivateImageWrites(std::span<DescriptorCache::PreparedBindings*> bindings);
|
||||||
|
void CommitBindings(CommandBuffer& buffer, vk::PipelineBindPoint pipeline_bind_point,
|
||||||
|
vk::PipelineLayout layout, DescriptorCache::PreparedBindings& bindings);
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#ifdef min
|
#ifdef min
|
||||||
#undef min
|
#undef min
|
||||||
@@ -49,8 +50,21 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
using TextureVariant = DescriptorCache::TextureVariant;
|
using TextureVariant = DescriptorCache::TextureVariant;
|
||||||
|
|
||||||
static void BindNullStorageBuffer(CommandBuffer& cmd_buffer, BufferView& dst) {
|
ImageWriteActivation ClassifyImageWriteActivation(VulkanImageType type) noexcept {
|
||||||
dst.buffer = &GetRenderContext().GetBufferCache().ObtainNullBuffer(cmd_buffer);
|
switch (type) {
|
||||||
|
case VulkanImageType::StorageTexture: return ImageWriteActivation::Implicit;
|
||||||
|
case VulkanImageType::VideoOut:
|
||||||
|
case VulkanImageType::DepthStencil:
|
||||||
|
case VulkanImageType::RenderTexture: return ImageWriteActivation::Explicit;
|
||||||
|
case VulkanImageType::Unknown:
|
||||||
|
case VulkanImageType::Texture: return ImageWriteActivation::Unsupported;
|
||||||
|
}
|
||||||
|
return ImageWriteActivation::Unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BindNullStorageBuffer(BufferView& dst) {
|
||||||
|
dst.owner = GetRenderContext().GetBufferCache().ObtainNullBuffer();
|
||||||
|
dst.buffer = dst.owner.get();
|
||||||
dst.offset = 0;
|
dst.offset = 0;
|
||||||
dst.range = 16;
|
dst.range = 16;
|
||||||
}
|
}
|
||||||
@@ -119,7 +133,7 @@ static void CopyNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& so
|
|||||||
std::copy_n(source.dwords.begin(), destination.size(), destination.begin());
|
std::copy_n(source.dwords.begin(), destination.size(), destination.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
static BufferView NativeStorageBuffer(uint64_t submit_id, CommandBuffer& command_buffer,
|
static BufferView NativeStorageBuffer(CommandBuffer& command_buffer,
|
||||||
const ShaderBufferResource& descriptor,
|
const ShaderBufferResource& descriptor,
|
||||||
const ShaderRecompiler::IR::BufferResource& resource) {
|
const ShaderRecompiler::IR::BufferResource& resource) {
|
||||||
BufferView result;
|
BufferView result;
|
||||||
@@ -132,7 +146,7 @@ static BufferView NativeStorageBuffer(uint64_t submit_id, CommandBuffer& command
|
|||||||
}
|
}
|
||||||
const auto size = stride != 0 ? static_cast<uint64_t>(stride) * records : records;
|
const auto size = stride != 0 ? static_cast<uint64_t>(stride) * records : records;
|
||||||
if (address == 0 || size == 0) {
|
if (address == 0 || size == 0) {
|
||||||
BindNullStorageBuffer(command_buffer, result);
|
BindNullStorageBuffer(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
const auto& graphics = GetRenderContext().GetGraphics();
|
const auto& graphics = GetRenderContext().GetGraphics();
|
||||||
@@ -142,25 +156,26 @@ static BufferView NativeStorageBuffer(uint64_t submit_id, CommandBuffer& command
|
|||||||
BufferCache::CACHING_PAGE_SIZE % alignment != 0) {
|
BufferCache::CACHING_PAGE_SIZE % alignment != 0) {
|
||||||
EXIT("storage buffer range or device alignment is unsupported\n");
|
EXIT("storage buffer range or device alignment is unsupported\n");
|
||||||
}
|
}
|
||||||
(void)submit_id;
|
|
||||||
auto binding = GetRenderContext().GetBufferCache().ObtainBuffer(
|
auto binding = GetRenderContext().GetBufferCache().ObtainBuffer(
|
||||||
command_buffer, address, size, resource.written, resource.read, resource.formatted);
|
command_buffer, address, size, resource.written, resource.read, resource.formatted);
|
||||||
if (binding.offset % alignment != 0) {
|
if (binding.host_data.empty() && binding.offset % alignment != 0) {
|
||||||
EXIT("storage buffer binding is not device-aligned\n");
|
EXIT("storage buffer binding is not device-aligned\n");
|
||||||
}
|
}
|
||||||
result.buffer = &binding.buffer;
|
result.owner = std::move(binding.buffer);
|
||||||
result.offset = binding.offset;
|
result.buffer = result.owner.get();
|
||||||
result.range = static_cast<vk::DeviceSize>(size);
|
result.offset = binding.offset;
|
||||||
|
result.range = static_cast<vk::DeviceSize>(size);
|
||||||
|
result.host_data = std::move(binding.host_data);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BufferView
|
static BufferView
|
||||||
NativeAddressBuffer(uint64_t submit_id, CommandBuffer& command_buffer,
|
NativeAddressBuffer(CommandBuffer& command_buffer,
|
||||||
const ShaderRecompiler::IR::AddressResource& resource,
|
const ShaderRecompiler::IR::AddressResource& resource,
|
||||||
const ShaderRecompiler::IR::ResourceSnapshot::Address& address) {
|
const ShaderRecompiler::IR::ResourceSnapshot::Address& address) {
|
||||||
BufferView result;
|
BufferView result;
|
||||||
if (address.binding_base == 0) {
|
if (address.binding_base == 0) {
|
||||||
BindNullStorageBuffer(command_buffer, result);
|
BindNullStorageBuffer(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (resource.written) {
|
if (resource.written) {
|
||||||
@@ -185,12 +200,13 @@ NativeAddressBuffer(uint64_t submit_id, CommandBuffer& command_buffer,
|
|||||||
BufferCache::GetBufferOffset(address.binding_base) % alignment != 0) {
|
BufferCache::GetBufferOffset(address.binding_base) % alignment != 0) {
|
||||||
EXIT("address resource range or alignment is unsupported\n");
|
EXIT("address resource range or alignment is unsupported\n");
|
||||||
}
|
}
|
||||||
(void)submit_id;
|
|
||||||
auto binding = GetRenderContext().GetBufferCache().ObtainBuffer(command_buffer,
|
auto binding = GetRenderContext().GetBufferCache().ObtainBuffer(command_buffer,
|
||||||
address.binding_base, size);
|
address.binding_base, size);
|
||||||
result.buffer = &binding.buffer;
|
result.owner = std::move(binding.buffer);
|
||||||
result.offset = binding.offset;
|
result.buffer = result.owner.get();
|
||||||
result.range = static_cast<vk::DeviceSize>(size);
|
result.offset = binding.offset;
|
||||||
|
result.range = static_cast<vk::DeviceSize>(size);
|
||||||
|
result.host_data = std::move(binding.host_data);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,8 +491,7 @@ void ValidateMetadataReuseTexture(const ShaderRecompiler::IR::ImageResource& res
|
|||||||
}
|
}
|
||||||
|
|
||||||
static DescriptorCache::TextureBinding
|
static DescriptorCache::TextureBinding
|
||||||
NativeTexture(uint64_t submit_id, CommandBuffer& command_buffer,
|
NativeTexture(CommandBuffer& command_buffer, const ShaderRecompiler::IR::ImageResource& resource,
|
||||||
const ShaderRecompiler::IR::ImageResource& resource,
|
|
||||||
const ShaderRecompiler::IR::DescriptorValue& value) {
|
const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||||
ShaderTextureResource descriptor;
|
ShaderTextureResource descriptor;
|
||||||
CopyNativeDescriptor(value, descriptor.fields);
|
CopyNativeDescriptor(value, descriptor.fields);
|
||||||
@@ -536,6 +551,7 @@ NativeTexture(uint64_t submit_id, CommandBuffer& command_buffer,
|
|||||||
VulkanImage* image = nullptr;
|
VulkanImage* image = nullptr;
|
||||||
int view = VulkanImage::VIEW_DEFAULT;
|
int view = VulkanImage::VIEW_DEFAULT;
|
||||||
vk::ImageView image_view = nullptr;
|
vk::ImageView image_view = nullptr;
|
||||||
|
bool external = false;
|
||||||
const bool check_depth = static_cast<Prospero::TileMode>(tile) == Prospero::TileMode::kDepth ||
|
const bool check_depth = static_cast<Prospero::TileMode>(tile) == Prospero::TileMode::kDepth ||
|
||||||
descriptor.MsaaDepth();
|
descriptor.MsaaDepth();
|
||||||
if (image == nullptr) {
|
if (image == nullptr) {
|
||||||
@@ -615,14 +631,12 @@ NativeTexture(uint64_t submit_id, CommandBuffer& command_buffer,
|
|||||||
if (image != nullptr && image_view == nullptr && image->image_view[view] == nullptr) {
|
if (image != nullptr && image_view == nullptr && image->image_view[view] == nullptr) {
|
||||||
EXIT("required cached texture image view is missing\n");
|
EXIT("required cached texture image view is missing\n");
|
||||||
}
|
}
|
||||||
if (storage && image != nullptr) {
|
|
||||||
GetRenderContext().GetTextureCache().MarkGpuWritten(*image);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (image == nullptr) {
|
if (image == nullptr) {
|
||||||
const auto video = Presentation::DisplayBufferFind(address);
|
const auto video = Presentation::DisplayBufferFind(address);
|
||||||
if (video.image != nullptr) {
|
if (video.image != nullptr) {
|
||||||
|
external = true;
|
||||||
if (storage) {
|
if (storage) {
|
||||||
const bool exact =
|
const bool exact =
|
||||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint &&
|
||||||
@@ -644,7 +658,6 @@ NativeTexture(uint64_t submit_id, CommandBuffer& command_buffer,
|
|||||||
}
|
}
|
||||||
image = video.image;
|
image = video.image;
|
||||||
view = VulkanImage::VIEW_STORAGE;
|
view = VulkanImage::VIEW_STORAGE;
|
||||||
GetRenderContext().GetTextureCache().MarkGpuWritten(*image);
|
|
||||||
} else {
|
} else {
|
||||||
const bool exact =
|
const bool exact =
|
||||||
IsSupportedSampledVideoOutView(resource, descriptor, *video.image) &&
|
IsSupportedSampledVideoOutView(resource, descriptor, *video.image) &&
|
||||||
@@ -678,7 +691,6 @@ NativeTexture(uint64_t submit_id, CommandBuffer& command_buffer,
|
|||||||
if (!storage && metadata_read) {
|
if (!storage && metadata_read) {
|
||||||
ValidateMetadataReuseTexture(resource, descriptor, size.size);
|
ValidateMetadataReuseTexture(resource, descriptor, size.size);
|
||||||
}
|
}
|
||||||
(void)submit_id;
|
|
||||||
(void)command_buffer;
|
(void)command_buffer;
|
||||||
ImageInfo info {};
|
ImageInfo info {};
|
||||||
info.address = address;
|
info.address = address;
|
||||||
@@ -714,7 +726,9 @@ NativeTexture(uint64_t submit_id, CommandBuffer& command_buffer,
|
|||||||
image_view != nullptr)) {
|
image_view != nullptr)) {
|
||||||
view = SelectSampledTextureArrayView(*image, view);
|
view = SelectSampledTextureArrayView(*image, view);
|
||||||
}
|
}
|
||||||
return {image, view, image_view};
|
return {image, view, image_view,
|
||||||
|
external ? std::shared_ptr<void> {}
|
||||||
|
: GetRenderContext().GetTextureCache().GetImageOwner(*image)};
|
||||||
}
|
}
|
||||||
|
|
||||||
static vk::Sampler NativeSampler(const ShaderRecompiler::IR::Program& program, uint32_t index,
|
static vk::Sampler NativeSampler(const ShaderRecompiler::IR::Program& program, uint32_t index,
|
||||||
@@ -742,10 +756,9 @@ static BufferView NativeUpload(CommandBuffer& command_buffer, std::span<const ui
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindDescriptors(uint64_t submit_id, CommandBuffer& buffer,
|
DescriptorCache::PreparedBindings
|
||||||
vk::PipelineBindPoint pipeline_bind_point, vk::PipelineLayout layout,
|
PrepareBindings(CommandBuffer& buffer, const ShaderStageRuntime& runtime,
|
||||||
const ShaderStageRuntime& runtime, vk::ShaderStageFlags vk_stage,
|
vk::ShaderStageFlags shader_stage, DescriptorCache::Stage stage) {
|
||||||
DescriptorCache::Stage stage) {
|
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
EXIT_IF(!runtime);
|
EXIT_IF(!runtime);
|
||||||
const auto& program = *runtime.program;
|
const auto& program = *runtime.program;
|
||||||
@@ -754,27 +767,30 @@ void BindDescriptors(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
if (!ShaderRecompiler::IR::ValidateResourceSpecialization(program, snapshot, &error)) {
|
if (!ShaderRecompiler::IR::ValidateResourceSpecialization(program, snapshot, &error)) {
|
||||||
EXIT("invalid native shader runtime snapshot: %s\n", error.c_str());
|
EXIT("invalid native shader runtime snapshot: %s\n", error.c_str());
|
||||||
}
|
}
|
||||||
auto vk_buffer = buffer.Handle();
|
|
||||||
const auto shader_stages = ShaderPipelineStages(vk_stage);
|
|
||||||
|
|
||||||
DescriptorCache::NativeDescriptors descriptors;
|
DescriptorCache::PreparedBindings prepared;
|
||||||
|
prepared.program = runtime.program;
|
||||||
|
prepared.snapshot = runtime.resources;
|
||||||
|
prepared.shader_stage = shader_stage;
|
||||||
|
prepared.stage = stage;
|
||||||
|
auto& descriptors = prepared.resources;
|
||||||
descriptors.buffers.reserve(program.info.buffers.size());
|
descriptors.buffers.reserve(program.info.buffers.size());
|
||||||
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||||
ShaderBufferResource descriptor;
|
ShaderBufferResource descriptor;
|
||||||
CopyNativeDescriptor(snapshot.buffers[i], descriptor.fields);
|
CopyNativeDescriptor(snapshot.buffers[i], descriptor.fields);
|
||||||
descriptors.buffers.push_back(
|
descriptors.buffers.push_back(
|
||||||
NativeStorageBuffer(submit_id, buffer, descriptor, program.info.buffers[i]));
|
NativeStorageBuffer(buffer, descriptor, program.info.buffers[i]));
|
||||||
}
|
}
|
||||||
descriptors.images.reserve(program.info.images.size());
|
descriptors.images.reserve(program.info.images.size());
|
||||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||||
const auto kind = program.info.images[i].kind;
|
const auto kind = program.info.images[i].kind;
|
||||||
if ((kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
if ((kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||||
kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint) &&
|
kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint) &&
|
||||||
vk_stage != vk::ShaderStageFlagBits::eCompute) {
|
shader_stage != vk::ShaderStageFlagBits::eCompute) {
|
||||||
EXIT("storage images are unsupported outside compute shaders\n");
|
EXIT("storage images are unsupported outside compute shaders\n");
|
||||||
}
|
}
|
||||||
descriptors.images.push_back(
|
descriptors.images.push_back(
|
||||||
NativeTexture(submit_id, buffer, program.info.images[i], snapshot.images[i]));
|
NativeTexture(buffer, program.info.images[i], snapshot.images[i]));
|
||||||
}
|
}
|
||||||
descriptors.samplers.reserve(program.info.samplers.size());
|
descriptors.samplers.reserve(program.info.samplers.size());
|
||||||
for (uint32_t i = 0; i < program.info.samplers.size(); i++) {
|
for (uint32_t i = 0; i < program.info.samplers.size(); i++) {
|
||||||
@@ -782,28 +798,150 @@ void BindDescriptors(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
}
|
}
|
||||||
descriptors.addresses.reserve(program.info.addresses.size());
|
descriptors.addresses.reserve(program.info.addresses.size());
|
||||||
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
||||||
descriptors.addresses.push_back(NativeAddressBuffer(
|
descriptors.addresses.push_back(
|
||||||
submit_id, buffer, program.info.addresses[i], snapshot.addresses[i]));
|
NativeAddressBuffer(buffer, program.info.addresses[i], snapshot.addresses[i]));
|
||||||
}
|
}
|
||||||
if (ShaderRecompiler::IR::FindBinding(
|
if (ShaderRecompiler::IR::FindBinding(
|
||||||
program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::FlattenedSrt) !=
|
program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::FlattenedSrt) !=
|
||||||
nullptr) {
|
nullptr) {
|
||||||
descriptors.flattened_srt = NativeUpload(buffer, snapshot.flattened_srt);
|
prepared.flattened_srt.assign(snapshot.flattened_srt.begin(), snapshot.flattened_srt.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> user_data;
|
prepared.user_data.reserve(program.bindings.user_data_registers.size());
|
||||||
user_data.reserve(program.bindings.user_data_registers.size());
|
|
||||||
for (const auto reg: program.bindings.user_data_registers) {
|
for (const auto reg: program.bindings.user_data_registers) {
|
||||||
user_data.push_back(snapshot.user_data[reg - program.user_data_base]);
|
prepared.user_data.push_back(snapshot.user_data[reg - program.user_data_base]);
|
||||||
}
|
|
||||||
if (ShaderRecompiler::IR::FindBinding(
|
|
||||||
program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::UserData) != nullptr) {
|
|
||||||
descriptors.user_data = NativeUpload(buffer, user_data);
|
|
||||||
}
|
}
|
||||||
if (ShaderRecompiler::IR::FindBinding(
|
if (ShaderRecompiler::IR::FindBinding(
|
||||||
program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::Gds) != nullptr) {
|
program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::Gds) != nullptr) {
|
||||||
descriptors.gds.buffer = &GetRenderContext().GetGdsBuffer().GetBuffer();
|
descriptors.gds.buffer = &GetRenderContext().GetGdsBuffer().GetBuffer();
|
||||||
const auto barrier = MakeGdsDependency(*descriptors.gds.buffer);
|
}
|
||||||
|
return prepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RebindBuffers(CommandBuffer& buffer, DescriptorCache::PreparedBindings& prepared) {
|
||||||
|
KYTY_PROFILER_FUNCTION();
|
||||||
|
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||||
|
const auto& program = *prepared.program;
|
||||||
|
const auto& snapshot = *prepared.snapshot;
|
||||||
|
auto& resources = prepared.resources;
|
||||||
|
|
||||||
|
resources.buffers.clear();
|
||||||
|
resources.buffers.reserve(program.info.buffers.size());
|
||||||
|
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||||
|
ShaderBufferResource descriptor;
|
||||||
|
CopyNativeDescriptor(snapshot.buffers[i], descriptor.fields);
|
||||||
|
resources.buffers.push_back(
|
||||||
|
NativeStorageBuffer(buffer, descriptor, program.info.buffers[i]));
|
||||||
|
}
|
||||||
|
resources.addresses.clear();
|
||||||
|
resources.addresses.reserve(program.info.addresses.size());
|
||||||
|
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
||||||
|
resources.addresses.push_back(
|
||||||
|
NativeAddressBuffer(buffer, program.info.addresses[i], snapshot.addresses[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RebindImages(CommandBuffer& buffer, DescriptorCache::PreparedBindings& prepared) {
|
||||||
|
KYTY_PROFILER_FUNCTION();
|
||||||
|
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||||
|
const auto& program = *prepared.program;
|
||||||
|
const auto& snapshot = *prepared.snapshot;
|
||||||
|
auto& images = prepared.resources.images;
|
||||||
|
images.clear();
|
||||||
|
images.reserve(program.info.images.size());
|
||||||
|
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||||
|
images.push_back(NativeTexture(buffer, program.info.images[i], snapshot.images[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivateImageWrites(std::span<DescriptorCache::PreparedBindings*> bindings) {
|
||||||
|
std::unordered_set<VulkanImage*> writable;
|
||||||
|
for (const auto* prepared: bindings) {
|
||||||
|
if (prepared == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
EXIT_IF(prepared->program == nullptr ||
|
||||||
|
prepared->resources.images.size() != prepared->program->info.images.size());
|
||||||
|
for (uint32_t i = 0; i < prepared->program->info.images.size(); i++) {
|
||||||
|
const auto& binding = prepared->resources.images[i];
|
||||||
|
EXIT_IF(binding.image == nullptr);
|
||||||
|
if (binding.owner != nullptr) {
|
||||||
|
auto current = GetRenderContext().GetTextureCache().GetImageOwner(*binding.image);
|
||||||
|
EXIT_IF(current.get() != binding.owner.get());
|
||||||
|
}
|
||||||
|
if (prepared->program->info.images[i].written) {
|
||||||
|
switch (ClassifyImageWriteActivation(binding.image->type)) {
|
||||||
|
case ImageWriteActivation::Implicit: break;
|
||||||
|
case ImageWriteActivation::Explicit: writable.insert(binding.image); break;
|
||||||
|
case ImageWriteActivation::Unsupported:
|
||||||
|
EXIT("unsupported writable image type: %u\n",
|
||||||
|
static_cast<uint32_t>(binding.image->type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto* image: writable) {
|
||||||
|
GetRenderContext().GetTextureCache().MarkGpuWritten(*image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RetainBindings(CommandBuffer& buffer,
|
||||||
|
const DescriptorCache::NativeDescriptors& resources) {
|
||||||
|
for (const auto& view: resources.buffers) {
|
||||||
|
if (view.owner != nullptr) {
|
||||||
|
buffer.RetainResourceUntilFence(view.owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& view: resources.addresses) {
|
||||||
|
if (view.owner != nullptr) {
|
||||||
|
buffer.RetainResourceUntilFence(view.owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& image: resources.images) {
|
||||||
|
if (image.owner != nullptr) {
|
||||||
|
buffer.RetainResourceUntilFence(image.owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CommitHostBuffers(CommandBuffer& buffer, std::vector<BufferView>& views) {
|
||||||
|
const auto alignment = GetRenderContext().GetGraphics().StorageMinAlignment();
|
||||||
|
EXIT_IF(alignment == 0);
|
||||||
|
for (auto& view: views) {
|
||||||
|
if (view.host_data.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
EXIT_IF(view.owner != nullptr || view.buffer != nullptr);
|
||||||
|
vk::DeviceSize uploaded_range = 0;
|
||||||
|
EXIT_IF(!GetRenderContext().GetBufferCache().UploadHostData(
|
||||||
|
buffer, view.host_data.data(), view.host_data.size(), alignment, view.buffer,
|
||||||
|
view.offset, uploaded_range));
|
||||||
|
EXIT_IF(uploaded_range != view.range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommitBindings(CommandBuffer& buffer, vk::PipelineBindPoint pipeline_bind_point,
|
||||||
|
vk::PipelineLayout layout, DescriptorCache::PreparedBindings& prepared) {
|
||||||
|
KYTY_PROFILER_FUNCTION();
|
||||||
|
EXIT_IF(prepared.program == nullptr || prepared.stage == DescriptorCache::Stage::Unknown ||
|
||||||
|
prepared.committed);
|
||||||
|
const auto& program = *prepared.program;
|
||||||
|
auto& descriptors = prepared.resources;
|
||||||
|
CommitHostBuffers(buffer, descriptors.buffers);
|
||||||
|
CommitHostBuffers(buffer, descriptors.addresses);
|
||||||
|
if (!prepared.flattened_srt.empty()) {
|
||||||
|
descriptors.flattened_srt = NativeUpload(buffer, prepared.flattened_srt);
|
||||||
|
}
|
||||||
|
if (ShaderRecompiler::IR::FindBinding(
|
||||||
|
program.bindings, ShaderRecompiler::IR::DescriptorBindingKind::UserData) != nullptr) {
|
||||||
|
descriptors.user_data = NativeUpload(buffer, prepared.user_data);
|
||||||
|
}
|
||||||
|
RetainBindings(buffer, descriptors);
|
||||||
|
|
||||||
|
auto vk_buffer = buffer.Handle();
|
||||||
|
const auto shader_stages = ShaderPipelineStages(prepared.shader_stage);
|
||||||
|
if (descriptors.gds.buffer != nullptr) {
|
||||||
|
const auto barrier = MakeGdsDependency(*descriptors.gds.buffer);
|
||||||
vk_buffer.pipelineBarrier(
|
vk_buffer.pipelineBarrier(
|
||||||
vk::PipelineStageFlagBits::eHost | vk::PipelineStageFlagBits::eTransfer |
|
vk::PipelineStageFlagBits::eHost | vk::PipelineStageFlagBits::eTransfer |
|
||||||
vk::PipelineStageFlagBits::eAllGraphics | vk::PipelineStageFlagBits::eComputeShader,
|
vk::PipelineStageFlagBits::eAllGraphics | vk::PipelineStageFlagBits::eComputeShader,
|
||||||
@@ -838,17 +976,19 @@ void BindDescriptors(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!program.bindings.descriptors.empty()) {
|
if (!program.bindings.descriptors.empty()) {
|
||||||
auto& set =
|
auto& set = GetRenderContext().GetDescriptorCache().GetDescriptor(
|
||||||
GetRenderContext().GetDescriptorCache().GetDescriptor(stage, program, descriptors);
|
prepared.stage, program, descriptors);
|
||||||
vk_buffer.bindDescriptorSets(pipeline_bind_point, layout, program.bindings.descriptor_set,
|
vk_buffer.bindDescriptorSets(pipeline_bind_point, layout, program.bindings.descriptor_set,
|
||||||
1, &set.set, 0, nullptr);
|
1, &set.set, 0, nullptr);
|
||||||
buffer.RecycleDescriptorAfterFence(set);
|
buffer.RecycleDescriptorAfterFence(set);
|
||||||
}
|
}
|
||||||
if (program.bindings.push_constant_size != 0) {
|
if (program.bindings.push_constant_size != 0) {
|
||||||
EXIT_IF(program.bindings.push_constant_size != user_data.size() * sizeof(uint32_t));
|
EXIT_IF(program.bindings.push_constant_size !=
|
||||||
vk_buffer.pushConstants(layout, vk_stage, program.bindings.push_constant_offset,
|
prepared.user_data.size() * sizeof(uint32_t));
|
||||||
program.bindings.push_constant_size, user_data.data());
|
vk_buffer.pushConstants(layout, prepared.shader_stage, program.bindings.push_constant_offset,
|
||||||
|
program.bindings.push_constant_size, prepared.user_data.data());
|
||||||
}
|
}
|
||||||
|
prepared.committed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|||||||
@@ -13,6 +13,12 @@
|
|||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
struct VulkanImage;
|
struct VulkanImage;
|
||||||
|
enum class VulkanImageType;
|
||||||
|
|
||||||
|
enum class ImageWriteActivation { Unsupported, Implicit, Explicit };
|
||||||
|
|
||||||
|
[[nodiscard]] ImageWriteActivation
|
||||||
|
ClassifyImageWriteActivation(VulkanImageType type) noexcept;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[nodiscard]] T DecodeNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& value) {
|
[[nodiscard]] T DecodeNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ public:
|
|||||||
[[nodiscard]] vk::CommandBuffer Handle() const;
|
[[nodiscard]] vk::CommandBuffer Handle() const;
|
||||||
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
||||||
[[nodiscard]] bool IsExecute() const { return m_execute; }
|
[[nodiscard]] bool IsExecute() const { return m_execute; }
|
||||||
[[nodiscard]] uint64_t GetRecordingGeneration() const { return m_recording_generation; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class BufferCache;
|
friend class BufferCache;
|
||||||
@@ -113,7 +112,6 @@ private:
|
|||||||
bool m_execute = false;
|
bool m_execute = false;
|
||||||
bool m_fence_waited = false;
|
bool m_fence_waited = false;
|
||||||
uint64_t m_submit_seq = 0;
|
uint64_t m_submit_seq = 0;
|
||||||
uint64_t m_recording_generation = 0;
|
|
||||||
uint32_t m_debug_op = 0;
|
uint32_t m_debug_op = 0;
|
||||||
uint64_t m_debug_submit_id = 0;
|
uint64_t m_debug_submit_id = 0;
|
||||||
uint32_t m_debug_arg0 = 0;
|
uint32_t m_debug_arg0 = 0;
|
||||||
|
|||||||
@@ -501,37 +501,33 @@ void RenderDispatchDirect(uint64_t submit_id, RenderCommandBuffer& buffer, uint3
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
auto& pipeline = GetRenderContext().GetPipelineCache().CreateComputePipeline(
|
||||||
const auto recording_generation = buffer.GetRecordingGeneration();
|
input_info, sh_ctx.GetCs(), cs_shader);
|
||||||
auto vk_buffer = buffer.Handle();
|
auto bindings = PrepareBindings(buffer, input_info.stage,
|
||||||
auto& pipeline = GetRenderContext().GetPipelineCache().CreateComputePipeline(
|
vk::ShaderStageFlagBits::eCompute,
|
||||||
input_info, sh_ctx.GetCs(), cs_shader);
|
DescriptorCache::Stage::Compute);
|
||||||
|
RebindBuffers(buffer, bindings);
|
||||||
|
RebindImages(buffer, bindings);
|
||||||
|
DescriptorCache::PreparedBindings* binding_sets[] = {&bindings};
|
||||||
|
ActivateImageWrites(binding_sets);
|
||||||
|
|
||||||
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
auto vk_buffer = buffer.Handle();
|
||||||
|
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline.pipeline_layout, bindings);
|
||||||
|
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
||||||
|
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
||||||
|
|
||||||
BindDescriptors(submit_id, buffer, vk::PipelineBindPoint::eCompute,
|
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
||||||
pipeline.pipeline_layout, input_info.stage,
|
has_storage_writes =
|
||||||
vk::ShaderStageFlagBits::eCompute, DescriptorCache::Stage::Compute);
|
std::any_of(
|
||||||
if (buffer.GetRecordingGeneration() != recording_generation) {
|
program.info.images.begin(), program.info.images.end(),
|
||||||
continue;
|
[](const auto& image) {
|
||||||
}
|
return image.written &&
|
||||||
|
(image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||||
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint);
|
||||||
|
}) ||
|
||||||
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
has_storage_writes;
|
||||||
has_storage_writes =
|
if (has_storage_writes) {
|
||||||
std::any_of(
|
ShaderWriteBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||||
program.info.images.begin(), program.info.images.end(),
|
|
||||||
[](const auto& image) {
|
|
||||||
return image.written &&
|
|
||||||
(image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
|
||||||
image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint);
|
|
||||||
}) ||
|
|
||||||
has_storage_writes;
|
|
||||||
if (has_storage_writes) {
|
|
||||||
ShaderWriteBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -487,11 +489,11 @@ struct DrawRenderState {
|
|||||||
uint32_t color_count = 0;
|
uint32_t color_count = 0;
|
||||||
bool ps_active = true;
|
bool ps_active = true;
|
||||||
VulkanFramebuffer* framebuffer = nullptr;
|
VulkanFramebuffer* framebuffer = nullptr;
|
||||||
vk::CommandBuffer vk_buffer = nullptr;
|
|
||||||
ShaderVertexInputInfo vs_input_info;
|
ShaderVertexInputInfo vs_input_info;
|
||||||
ShaderPixelInputInfo ps_input_info;
|
ShaderPixelInputInfo ps_input_info;
|
||||||
std::span<const uint32_t> vs_shader;
|
std::span<const uint32_t> vs_shader;
|
||||||
std::span<const uint32_t> ps_shader;
|
std::span<const uint32_t> ps_shader;
|
||||||
|
std::vector<std::shared_ptr<void>> target_owners;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DrawCallInfo {
|
struct DrawCallInfo {
|
||||||
@@ -558,6 +560,16 @@ struct DrawIndexBufferSource {
|
|||||||
vk::IndexType type = vk::IndexType::eUint16;
|
vk::IndexType type = vk::IndexType::eUint16;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PreparedIndexBuffer {
|
||||||
|
std::shared_ptr<VulkanBuffer> buffer;
|
||||||
|
const void* host_data = nullptr;
|
||||||
|
std::vector<uint8_t> owned_data;
|
||||||
|
uint64_t address = 0;
|
||||||
|
uint64_t size = 0;
|
||||||
|
vk::DeviceSize offset = 0;
|
||||||
|
vk::IndexType type = vk::IndexType::eUint16;
|
||||||
|
};
|
||||||
|
|
||||||
static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer) {
|
static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer) {
|
||||||
return (buffer.stride != 0 ? static_cast<uint64_t>(buffer.stride) * buffer.num_records
|
return (buffer.stride != 0 ? static_cast<uint64_t>(buffer.stride) * buffer.num_records
|
||||||
: buffer.num_records);
|
: buffer.num_records);
|
||||||
@@ -626,10 +638,12 @@ static bool PrepareDrawRenderState(uint64_t submit_id, RenderCommandBuffer& buff
|
|||||||
ResolveRenderDepthTarget(submit_id, buffer, state.depth_info);
|
ResolveRenderDepthTarget(submit_id, buffer, state.depth_info);
|
||||||
|
|
||||||
if (ResolveColorTargets(submit_id, buffer, render_target_slice_offset)) {
|
if (ResolveColorTargets(submit_id, buffer, render_target_slice_offset)) {
|
||||||
MarkRenderTargetGpuWritten(state.depth_info);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MarkRenderTargetGpuWritten(state.depth_info);
|
if (state.depth_info.vulkan_buffer != nullptr) {
|
||||||
|
state.target_owners.push_back(
|
||||||
|
GetRenderContext().GetTextureCache().GetImageOwner(*state.depth_info.vulkan_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
if (log_setup_phases) {
|
if (log_setup_phases) {
|
||||||
LogDrawPhase(draw.name, "ResolveRenderColorTarget");
|
LogDrawPhase(draw.name, "ResolveRenderColorTarget");
|
||||||
@@ -640,7 +654,12 @@ static bool PrepareDrawRenderState(uint64_t submit_id, RenderCommandBuffer& buff
|
|||||||
ResolveRenderColorTarget(submit_id, buffer, state.color_info[state.color_count],
|
ResolveRenderColorTarget(submit_id, buffer, state.color_info[state.color_count],
|
||||||
render_target_slice_offset, slot);
|
render_target_slice_offset, slot);
|
||||||
if (state.color_info[state.color_count].vulkan_buffer != nullptr) {
|
if (state.color_info[state.color_count].vulkan_buffer != nullptr) {
|
||||||
MarkRenderTargetGpuWritten(state.color_info[state.color_count]);
|
if (state.color_info[state.color_count].type == RenderColorType::RenderTexture) {
|
||||||
|
state.target_owners.push_back(GetRenderContext()
|
||||||
|
.GetTextureCache()
|
||||||
|
.GetImageOwner(*state.color_info[state.color_count]
|
||||||
|
.vulkan_buffer));
|
||||||
|
}
|
||||||
state.color_count++;
|
state.color_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -669,8 +688,6 @@ static bool PrepareDrawRenderState(uint64_t submit_id, RenderCommandBuffer& buff
|
|||||||
EXIT_NOT_IMPLEMENTED(state.framebuffer == nullptr);
|
EXIT_NOT_IMPLEMENTED(state.framebuffer == nullptr);
|
||||||
EXIT_NOT_IMPLEMENTED(state.framebuffer->render_pass == nullptr);
|
EXIT_NOT_IMPLEMENTED(state.framebuffer->render_pass == nullptr);
|
||||||
|
|
||||||
state.vk_buffer = buffer.Handle();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,56 +731,138 @@ static void RefreshShaders(RenderCommandBuffer& buffer, const DrawCallInfo& draw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BindDrawVertexBuffers(uint64_t submit_id, RenderCommandBuffer& buffer,
|
static std::vector<BufferBinding>
|
||||||
const DrawCallInfo& draw, vk::CommandBuffer vk_buffer,
|
PrepareVertexBuffers(uint64_t submit_id, RenderCommandBuffer& buffer, const DrawCallInfo& draw,
|
||||||
const ShaderVertexInputInfo& vs_input_info) {
|
const ShaderVertexInputInfo& vs_input_info) {
|
||||||
EXIT_IF(draw.name == nullptr);
|
EXIT_IF(draw.name == nullptr);
|
||||||
(void)submit_id;
|
(void)submit_id;
|
||||||
|
|
||||||
LogDrawPhase(draw.name, "BindVertexBuffers");
|
LogDrawPhase(draw.name, "PrepareVertexBuffers");
|
||||||
|
std::vector<BufferBinding> bindings;
|
||||||
|
bindings.reserve(vs_input_info.buffers_num);
|
||||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||||
const auto& b = vs_input_info.buffers[i];
|
const auto& b = vs_input_info.buffers[i];
|
||||||
uint64_t addr = b.addr;
|
const auto size = VertexBufferDescriptorSize(b);
|
||||||
uint64_t size = VertexBufferDescriptorSize(b);
|
|
||||||
VulkanBuffer* vertices = nullptr;
|
|
||||||
vk::DeviceSize offset = 0;
|
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
vertices = &GetRenderContext().GetBufferCache().ObtainNullBuffer(buffer);
|
bindings.push_back({GetRenderContext().GetBufferCache().ObtainNullBuffer(), 0});
|
||||||
} else {
|
} else {
|
||||||
auto binding = GetRenderContext().GetBufferCache().ObtainBuffer(buffer, addr, size);
|
bindings.push_back(
|
||||||
vertices = &binding.buffer;
|
GetRenderContext().GetBufferCache().ObtainBuffer(buffer, b.addr, size));
|
||||||
offset = binding.offset;
|
|
||||||
}
|
}
|
||||||
EXIT_NOT_IMPLEMENTED(vertices == nullptr);
|
}
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
vk_buffer.bindVertexBuffers(i, 1, &vertices->buffer, &offset);
|
static void RebindVertexBuffers(RenderCommandBuffer& buffer,
|
||||||
|
const ShaderVertexInputInfo& vs_input_info,
|
||||||
|
std::vector<BufferBinding>& bindings) {
|
||||||
|
EXIT_IF(bindings.size() != static_cast<size_t>(vs_input_info.buffers_num));
|
||||||
|
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||||
|
const auto& vertex = vs_input_info.buffers[i];
|
||||||
|
const auto size = VertexBufferDescriptorSize(vertex);
|
||||||
|
bindings[i] = size == 0
|
||||||
|
? BufferBinding {GetRenderContext().GetBufferCache().ObtainNullBuffer(), 0}
|
||||||
|
: GetRenderContext().GetBufferCache().ObtainBuffer(buffer, vertex.addr, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BindDrawIndexBuffer(RenderCommandBuffer& buffer, vk::CommandBuffer vk_buffer,
|
static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffer,
|
||||||
const DrawIndexBufferSource& source) {
|
const DrawIndexBufferSource& source) {
|
||||||
|
PreparedIndexBuffer prepared;
|
||||||
if (!source.enabled) {
|
if (!source.enabled) {
|
||||||
return;
|
return prepared;
|
||||||
}
|
}
|
||||||
EXIT_IF(source.size == 0);
|
EXIT_IF(source.size == 0);
|
||||||
|
prepared.host_data = source.host_data;
|
||||||
VulkanBuffer* index_buffer = nullptr;
|
prepared.address = source.address;
|
||||||
vk::DeviceSize index_offset = 0;
|
prepared.size = source.size;
|
||||||
|
prepared.type = source.type;
|
||||||
if (source.host_data != nullptr) {
|
if (source.host_data != nullptr) {
|
||||||
|
return prepared;
|
||||||
|
} else {
|
||||||
|
auto binding = GetRenderContext().GetBufferCache().ObtainBuffer(
|
||||||
|
buffer, source.address, source.size);
|
||||||
|
prepared.buffer = std::move(binding.buffer);
|
||||||
|
prepared.owned_data = std::move(binding.host_data);
|
||||||
|
prepared.offset = binding.offset;
|
||||||
|
}
|
||||||
|
return prepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RebindIndexBuffer(RenderCommandBuffer& buffer, PreparedIndexBuffer& prepared) {
|
||||||
|
if (prepared.size == 0 || prepared.host_data != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto binding = GetRenderContext().GetBufferCache().ObtainBuffer(
|
||||||
|
buffer, prepared.address, prepared.size);
|
||||||
|
prepared.buffer = std::move(binding.buffer);
|
||||||
|
prepared.owned_data = std::move(binding.host_data);
|
||||||
|
prepared.offset = binding.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RevalidateDrawTargets(DrawRenderState& state) {
|
||||||
|
auto old_owners = std::move(state.target_owners);
|
||||||
|
std::vector<std::shared_ptr<void>> current_owners;
|
||||||
|
auto validate = [&](VulkanImage& image) {
|
||||||
|
auto owner = GetRenderContext().GetTextureCache().GetImageOwner(image);
|
||||||
|
const auto found = std::find_if(old_owners.begin(), old_owners.end(), [&](const auto& old) {
|
||||||
|
return old.get() == owner.get();
|
||||||
|
});
|
||||||
|
EXIT_IF(found == old_owners.end());
|
||||||
|
current_owners.push_back(std::move(owner));
|
||||||
|
};
|
||||||
|
if (state.depth_info.vulkan_buffer != nullptr) {
|
||||||
|
validate(*state.depth_info.vulkan_buffer);
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < state.color_count; i++) {
|
||||||
|
if (state.color_info[i].type == RenderColorType::RenderTexture) {
|
||||||
|
validate(*state.color_info[i].vulkan_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.target_owners = std::move(current_owners);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CommitVertexBuffers(RenderCommandBuffer& buffer, vk::CommandBuffer vk_buffer,
|
||||||
|
std::vector<BufferBinding>& bindings) {
|
||||||
|
for (uint32_t slot = 0; slot < bindings.size(); slot++) {
|
||||||
|
auto& binding = bindings[slot];
|
||||||
|
VulkanBuffer* vertex_buffer = binding.buffer.get();
|
||||||
|
vk::DeviceSize vertex_offset = binding.offset;
|
||||||
|
if (!binding.host_data.empty()) {
|
||||||
|
vk::DeviceSize range = 0;
|
||||||
|
EXIT_IF(!GetRenderContext().GetBufferCache().UploadHostData(
|
||||||
|
buffer, binding.host_data.data(), binding.host_data.size(), 16, vertex_buffer,
|
||||||
|
vertex_offset, range));
|
||||||
|
} else {
|
||||||
|
buffer.RetainResourceUntilFence(binding.buffer);
|
||||||
|
}
|
||||||
|
EXIT_IF(vertex_buffer == nullptr);
|
||||||
|
vk_buffer.bindVertexBuffers(slot, 1, &vertex_buffer->buffer, &vertex_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CommitIndexBuffer(RenderCommandBuffer& buffer, vk::CommandBuffer vk_buffer,
|
||||||
|
const PreparedIndexBuffer& prepared) {
|
||||||
|
if (prepared.size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VulkanBuffer* index_buffer = prepared.buffer.get();
|
||||||
|
vk::DeviceSize index_offset = prepared.offset;
|
||||||
|
const void* host_data = prepared.host_data;
|
||||||
|
if (host_data == nullptr && !prepared.owned_data.empty()) {
|
||||||
|
host_data = prepared.owned_data.data();
|
||||||
|
}
|
||||||
|
if (host_data != nullptr) {
|
||||||
vk::DeviceSize range = 0;
|
vk::DeviceSize range = 0;
|
||||||
if (!GetRenderContext().GetBufferCache().UploadHostData(
|
if (!GetRenderContext().GetBufferCache().UploadHostData(
|
||||||
buffer, source.host_data, source.size, 16, index_buffer, index_offset, range)) {
|
buffer, host_data, prepared.size, 16, index_buffer, index_offset, range)) {
|
||||||
EXIT("failed to upload host index buffer\n");
|
EXIT("failed to upload host index buffer\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto binding =
|
buffer.RetainResourceUntilFence(prepared.buffer);
|
||||||
GetRenderContext().GetBufferCache().ObtainBuffer(buffer, source.address, source.size);
|
|
||||||
index_buffer = &binding.buffer;
|
|
||||||
index_offset = binding.offset;
|
|
||||||
}
|
}
|
||||||
EXIT_IF(index_buffer == nullptr);
|
EXIT_IF(index_buffer == nullptr);
|
||||||
vk_buffer.bindIndexBuffer(index_buffer->buffer, index_offset, source.type);
|
vk_buffer.bindIndexBuffer(index_buffer->buffer, index_offset, prepared.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LogDrawStateIfNeeded(const RenderCommandBuffer& buffer, const DrawCallInfo& draw,
|
static void LogDrawStateIfNeeded(const RenderCommandBuffer& buffer, const DrawCallInfo& draw,
|
||||||
@@ -865,88 +964,108 @@ static void ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer& buffer,
|
|||||||
bool set_bind_debug, bool set_auto_debug) {
|
bool set_bind_debug, bool set_auto_debug) {
|
||||||
EXIT_IF(draw.name == nullptr);
|
EXIT_IF(draw.name == nullptr);
|
||||||
auto& ucfg = buffer.GetUserConfig();
|
auto& ucfg = buffer.GetUserConfig();
|
||||||
|
if (log_pipeline_phase) {
|
||||||
|
LogDrawPhase(draw.name, "CreatePipeline");
|
||||||
|
}
|
||||||
|
auto& pipeline = GetRenderContext().GetPipelineCache().CreateGraphicsPipeline(
|
||||||
|
*state.framebuffer, state.color_info, state.color_count, state.depth_info,
|
||||||
|
state.vs_input_info, buffer, &state.ps_input_info, topology, state.ps_active,
|
||||||
|
state.vs_shader, state.ps_shader);
|
||||||
|
|
||||||
for (;;) {
|
LogDrawPhase(draw.name, "PrepareBindingsVS");
|
||||||
const auto recording_generation = buffer.GetRecordingGeneration();
|
auto vs_bindings = PrepareBindings(buffer, state.vs_input_info.stage,
|
||||||
if (log_pipeline_phase) {
|
vk::ShaderStageFlagBits::eVertex,
|
||||||
LogDrawPhase(draw.name, "CreatePipeline");
|
DescriptorCache::Stage::Vertex);
|
||||||
}
|
std::optional<DescriptorCache::PreparedBindings> ps_bindings;
|
||||||
auto& pipeline = GetRenderContext().GetPipelineCache().CreateGraphicsPipeline(
|
if (state.ps_active) {
|
||||||
*state.framebuffer, state.color_info, state.color_count, state.depth_info,
|
LogDrawPhase(draw.name, "PrepareBindingsPS");
|
||||||
state.vs_input_info, buffer, &state.ps_input_info, topology, state.ps_active,
|
ps_bindings.emplace(PrepareBindings(buffer, state.ps_input_info.stage,
|
||||||
state.vs_shader, state.ps_shader);
|
vk::ShaderStageFlagBits::eFragment,
|
||||||
|
DescriptorCache::Stage::Pixel));
|
||||||
|
}
|
||||||
|
auto vertex_bindings = PrepareVertexBuffers(submit_id, buffer, draw, state.vs_input_info);
|
||||||
|
auto index_binding = PrepareIndexBuffer(buffer, index_source);
|
||||||
|
|
||||||
if (set_bind_debug) {
|
// The discovery pass above can merge cache allocations. Rebind every buffer only after the
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x100u);
|
// complete draw resource set is known.
|
||||||
}
|
RebindBuffers(buffer, vs_bindings);
|
||||||
state.vk_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.pipeline);
|
if (ps_bindings.has_value()) {
|
||||||
const auto dynamic_params = BuildGraphicsDynamicParams(buffer, state.color_info,
|
RebindBuffers(buffer, *ps_bindings);
|
||||||
state.color_count, state.depth_info);
|
}
|
||||||
SetDynamicParams(buffer, state.vk_buffer, dynamic_params);
|
RebindVertexBuffers(buffer, state.vs_input_info, vertex_bindings);
|
||||||
|
RebindIndexBuffer(buffer, index_binding);
|
||||||
|
RebindImages(buffer, vs_bindings);
|
||||||
|
if (ps_bindings.has_value()) {
|
||||||
|
RebindImages(buffer, *ps_bindings);
|
||||||
|
}
|
||||||
|
|
||||||
// EXIT_NOT_IMPLEMENTED(vs_input_info.buffers_num > 1);
|
// Resource preparation above may synchronously finish and restart the scheduler. From this
|
||||||
BindDrawVertexBuffers(submit_id, buffer, draw, state.vk_buffer, state.vs_input_info);
|
// point onward, every operation targets the current command buffer and cannot touch guest
|
||||||
|
// memory.
|
||||||
|
RevalidateDrawTargets(state);
|
||||||
|
DescriptorCache::PreparedBindings* binding_sets[] = {
|
||||||
|
&vs_bindings, ps_bindings.has_value() ? &*ps_bindings : nullptr};
|
||||||
|
ActivateImageWrites(binding_sets);
|
||||||
|
MarkRenderTargetGpuWritten(state.depth_info);
|
||||||
|
for (uint32_t i = 0; i < state.color_count; i++) {
|
||||||
|
MarkRenderTargetGpuWritten(state.color_info[i]);
|
||||||
|
}
|
||||||
|
for (const auto& owner: state.target_owners) {
|
||||||
|
buffer.RetainResourceUntilFence(owner);
|
||||||
|
}
|
||||||
|
|
||||||
LogDrawPhase(draw.name, "BindDescriptorsVS");
|
auto vk_buffer = buffer.Handle();
|
||||||
|
if (set_bind_debug) {
|
||||||
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x100u);
|
||||||
|
}
|
||||||
|
if (set_auto_debug) {
|
||||||
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x200u);
|
||||||
|
}
|
||||||
|
CommitVertexBuffers(buffer, vk_buffer, vertex_bindings);
|
||||||
|
CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline.pipeline_layout,
|
||||||
|
vs_bindings);
|
||||||
|
if (ps_bindings.has_value()) {
|
||||||
if (set_auto_debug) {
|
if (set_auto_debug) {
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x200u);
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x300u);
|
||||||
}
|
}
|
||||||
BindDescriptors(submit_id, buffer, vk::PipelineBindPoint::eGraphics,
|
CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline.pipeline_layout,
|
||||||
pipeline.pipeline_layout, state.vs_input_info.stage,
|
*ps_bindings);
|
||||||
vk::ShaderStageFlagBits::eVertex, DescriptorCache::Stage::Vertex);
|
}
|
||||||
|
CommitIndexBuffer(buffer, vk_buffer, index_binding);
|
||||||
|
|
||||||
if (state.ps_active) {
|
const auto dynamic_params = BuildGraphicsDynamicParams(buffer, state.color_info,
|
||||||
LogDrawPhase(draw.name, "BindDescriptorsPS");
|
state.color_count, state.depth_info);
|
||||||
if (set_auto_debug) {
|
SetDynamicParams(buffer, vk_buffer, dynamic_params);
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x300u);
|
|
||||||
}
|
|
||||||
BindDescriptors(submit_id, buffer, vk::PipelineBindPoint::eGraphics,
|
|
||||||
pipeline.pipeline_layout, state.ps_input_info.stage,
|
|
||||||
vk::ShaderStageFlagBits::eFragment, DescriptorCache::Stage::Pixel);
|
|
||||||
}
|
|
||||||
if (buffer.GetRecordingGeneration() != recording_generation) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Index data may use this command buffer's host stream. Resolve it only after the other
|
|
||||||
// fault-capable bindings, and rebuild it whenever a fault reset changes the generation.
|
|
||||||
BindDrawIndexBuffer(buffer, state.vk_buffer, index_source);
|
|
||||||
if (buffer.GetRecordingGeneration() != recording_generation) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogDrawPhase(draw.name, "BeginRenderPass");
|
LogDrawPhase(draw.name, "BeginRenderPass");
|
||||||
if (set_auto_debug) {
|
if (set_auto_debug) {
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x400u);
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x400u);
|
||||||
}
|
}
|
||||||
buffer.BeginRenderPass(*state.framebuffer, state.color_info, state.color_count,
|
buffer.BeginRenderPass(*state.framebuffer, state.color_info, state.color_count,
|
||||||
state.depth_info);
|
state.depth_info);
|
||||||
if (set_auto_debug) {
|
vk_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.pipeline);
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x500u);
|
if (set_auto_debug) {
|
||||||
}
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x500u);
|
||||||
|
}
|
||||||
|
EmitDrawPrimitives(ucfg, vk_buffer, state.vs_input_info, draw, emit);
|
||||||
|
|
||||||
EmitDrawPrimitives(ucfg, state.vk_buffer, state.vs_input_info, draw, emit);
|
if (set_auto_debug) {
|
||||||
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x600u);
|
||||||
if (set_auto_debug) {
|
}
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x600u);
|
buffer.EndRenderPass();
|
||||||
}
|
vk::PipelineStageFlags shader_write_stages = {};
|
||||||
buffer.EndRenderPass();
|
if (HasShaderBufferWrites(state.vs_input_info.stage)) {
|
||||||
vk::PipelineStageFlags shader_write_stages = {};
|
shader_write_stages |= vk::PipelineStageFlagBits::eVertexShader;
|
||||||
if (HasShaderBufferWrites(state.vs_input_info.stage)) {
|
}
|
||||||
shader_write_stages |= vk::PipelineStageFlagBits::eVertexShader;
|
if (state.ps_active && HasShaderBufferWrites(state.ps_input_info.stage)) {
|
||||||
}
|
shader_write_stages |= vk::PipelineStageFlagBits::eFragmentShader;
|
||||||
if (state.ps_active) {
|
}
|
||||||
if (HasShaderBufferWrites(state.ps_input_info.stage)) {
|
if (shader_write_stages) {
|
||||||
shader_write_stages |= vk::PipelineStageFlagBits::eFragmentShader;
|
ShaderWriteBarrier(vk_buffer, shader_write_stages);
|
||||||
}
|
}
|
||||||
}
|
LogDrawPhase(draw.name, "EndRenderPass");
|
||||||
if (shader_write_stages) {
|
if (set_auto_debug) {
|
||||||
ShaderWriteBarrier(state.vk_buffer, shader_write_stages);
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x700u);
|
||||||
}
|
|
||||||
LogDrawPhase(draw.name, "EndRenderPass");
|
|
||||||
if (set_auto_debug) {
|
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x700u);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -340,6 +340,17 @@ VulkanImage& TextureCache::PublishImage(CommandBuffer& command,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<void> TextureCache::GetImageOwner(const VulkanImage& image) {
|
||||||
|
FaultSafeTextureLock lock(this, m_lock);
|
||||||
|
const auto owner = std::ranges::find_if(
|
||||||
|
m_images, [&](const auto& cached) { return cached->image == ℑ });
|
||||||
|
if (owner == m_images.end()) {
|
||||||
|
EXIT("TextureCache: image has no cache owner, image=%p\n",
|
||||||
|
static_cast<const void*>(&image));
|
||||||
|
}
|
||||||
|
return *owner;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<TextureCache::CachedImage*>
|
std::vector<TextureCache::CachedImage*>
|
||||||
TextureCache::FindImagesInRegionLocked(uint64_t vaddr, uint64_t size, bool page_overlap) {
|
TextureCache::FindImagesInRegionLocked(uint64_t vaddr, uint64_t size, bool page_overlap) {
|
||||||
return page_overlap ? m_image_owner_index.QueryCandidates(vaddr, size)
|
return page_overlap ? m_image_owner_index.QueryCandidates(vaddr, size)
|
||||||
@@ -1484,7 +1495,7 @@ VulkanImage& TextureCache::FindTexture(CommandBuffer& command, const ImageInfo&
|
|||||||
return *storage_match->image;
|
return *storage_match->image;
|
||||||
}
|
}
|
||||||
if (!storage_retire.empty()) {
|
if (!storage_retire.empty()) {
|
||||||
// shadPS4 resolves a modified cached mip before replacing it with the containing image.
|
// Resolve a modified cached mip before replacing it with the containing image.
|
||||||
// Kyty does not yet copy between those independently allocated Vulkan images, so use the
|
// Kyty does not yet copy between those independently allocated Vulkan images, so use the
|
||||||
// existing synchronized tiled readback seam and rebuild the complete chain from coherent
|
// existing synchronized tiled readback seam and rebuild the complete chain from coherent
|
||||||
// guest backing. This is an uncommon ownership transition, not a frame lookup fast path.
|
// guest backing. This is an uncommon ownership transition, not a frame lookup fast path.
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ public:
|
|||||||
|
|
||||||
VulkanImage& GetDummySampledTexture(bool uint_format, bool image_3d);
|
VulkanImage& GetDummySampledTexture(bool uint_format, bool image_3d);
|
||||||
VulkanImage& GetDummyStorageTexture(bool uint_format, bool image_3d);
|
VulkanImage& GetDummyStorageTexture(bool uint_format, bool image_3d);
|
||||||
|
[[nodiscard]] std::shared_ptr<void> GetImageOwner(const VulkanImage& image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CachedImage;
|
struct CachedImage;
|
||||||
|
|||||||
@@ -10557,6 +10557,56 @@ void CheckBufferCacheRangeMerge() {
|
|||||||
std::printf("[host] %-32s ok\n", "BufferCacheRangeMerge");
|
std::printf("[host] %-32s ok\n", "BufferCacheRangeMerge");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckBufferBindingStabilization() {
|
||||||
|
constexpr std::array<BufferCacheRange, 4> requests{{
|
||||||
|
{0x10000, 0x4000},
|
||||||
|
{0x12000, 0x6000},
|
||||||
|
{0x17000, 0x5000},
|
||||||
|
{0x11000, 0x1000},
|
||||||
|
}};
|
||||||
|
BufferCacheRange allocation = requests.front();
|
||||||
|
for (const auto request : std::span{requests}.subspan(1)) {
|
||||||
|
Require("BufferBindingStabilization", "discovery",
|
||||||
|
MergeOverlappingBufferCacheRange(allocation, request),
|
||||||
|
"overlapping discovery request did not join the cache allocation");
|
||||||
|
}
|
||||||
|
Require("BufferBindingStabilization", "canonical range",
|
||||||
|
allocation.address == 0x10000 && allocation.size == 0xc000,
|
||||||
|
"discovery did not produce the complete cache allocation");
|
||||||
|
|
||||||
|
for (const auto request : requests) {
|
||||||
|
const auto before = allocation;
|
||||||
|
Require("BufferBindingStabilization", "second pass",
|
||||||
|
MergeOverlappingBufferCacheRange(allocation, request) &&
|
||||||
|
allocation.address == before.address &&
|
||||||
|
allocation.size == before.size,
|
||||||
|
"second-pass binding changed a fully discovered allocation");
|
||||||
|
}
|
||||||
|
std::printf("[host] %-32s ok\n", "BufferBindingStabilization");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckImageWriteActivationPolicy() {
|
||||||
|
Require("ImageWriteActivationPolicy", "standalone storage",
|
||||||
|
ClassifyImageWriteActivation(VulkanImageType::StorageTexture) ==
|
||||||
|
ImageWriteActivation::Implicit,
|
||||||
|
"standalone storage image was sent through target write activation");
|
||||||
|
for (const auto type : {VulkanImageType::VideoOut,
|
||||||
|
VulkanImageType::DepthStencil,
|
||||||
|
VulkanImageType::RenderTexture}) {
|
||||||
|
Require("ImageWriteActivationPolicy", "cache target",
|
||||||
|
ClassifyImageWriteActivation(type) ==
|
||||||
|
ImageWriteActivation::Explicit,
|
||||||
|
"writable cache target skipped explicit write activation");
|
||||||
|
}
|
||||||
|
for (const auto type : {VulkanImageType::Unknown, VulkanImageType::Texture}) {
|
||||||
|
Require("ImageWriteActivationPolicy", "unsupported",
|
||||||
|
ClassifyImageWriteActivation(type) ==
|
||||||
|
ImageWriteActivation::Unsupported,
|
||||||
|
"unsupported writable image type received an activation policy");
|
||||||
|
}
|
||||||
|
std::printf("[host] %-32s ok\n", "ImageWriteActivationPolicy");
|
||||||
|
}
|
||||||
|
|
||||||
ShaderRecompiler::IR::ImageResource BasicStorageTextureResource() {
|
ShaderRecompiler::IR::ImageResource BasicStorageTextureResource() {
|
||||||
ShaderRecompiler::IR::ImageResource resource{};
|
ShaderRecompiler::IR::ImageResource resource{};
|
||||||
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImage;
|
resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImage;
|
||||||
@@ -14245,6 +14295,8 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--buffer-cache-range-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--buffer-cache-range-only") == 0) {
|
||||||
CheckBufferCacheRangeMerge();
|
CheckBufferCacheRangeMerge();
|
||||||
|
CheckBufferBindingStabilization();
|
||||||
|
CheckImageWriteActivationPolicy();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--storage-bgra-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--storage-bgra-only") == 0) {
|
||||||
@@ -14296,6 +14348,8 @@ int main(int argc, char **argv) {
|
|||||||
CheckSampledDepthResource();
|
CheckSampledDepthResource();
|
||||||
CheckSampledDepthDescriptor();
|
CheckSampledDepthDescriptor();
|
||||||
CheckBufferCacheRangeMerge();
|
CheckBufferCacheRangeMerge();
|
||||||
|
CheckBufferBindingStabilization();
|
||||||
|
CheckImageWriteActivationPolicy();
|
||||||
CheckBasicStorageTextureDescriptor();
|
CheckBasicStorageTextureDescriptor();
|
||||||
CheckStorageTextureLinearUploadLayout();
|
CheckStorageTextureLinearUploadLayout();
|
||||||
CheckStorageTextureDepthTileUploadLayout();
|
CheckStorageTextureDepthTileUploadLayout();
|
||||||
|
|||||||
Reference in New Issue
Block a user