mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-30 11:15:51 +00:00
Compare commits
7 Commits
e46dd1bd5f
...
f9560a2594
| Author | SHA1 | Date | |
|---|---|---|---|
| f9560a2594 | |||
| 0dee4b7711 | |||
| 119c41f614 | |||
| 67ce32c2d1 | |||
| 3f1ec49f65 | |||
| a8f7ddc958 | |||
| fe939bb14d |
@@ -115,7 +115,8 @@ void BufferCache<P>::TickFrame() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
||||
if (memory_tracker.IsRegionGpuModified(device_addr, size) ||
|
||||
IntersectsInPlaceWrites(device_addr, size)) {
|
||||
ClearDownload(device_addr, size);
|
||||
ForgetGpuModifiedRange(device_addr, size);
|
||||
}
|
||||
@@ -203,6 +204,17 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<WindowBufferRef> src_window;
|
||||
std::optional<WindowBufferRef> dst_window;
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
src_window = TryObtainWindowBuffer(*cpu_src_address, static_cast<u32>(amount),
|
||||
UNIFIED_COPY_BINDING_ALIGNMENT,
|
||||
ObtainBufferOperation::DoNothing);
|
||||
dst_window = TryObtainWindowBuffer(*cpu_dest_address, static_cast<u32>(amount),
|
||||
UNIFIED_COPY_BINDING_ALIGNMENT,
|
||||
ObtainBufferOperation::DoNothing);
|
||||
}
|
||||
|
||||
ClearDownload(*cpu_dest_address, amount);
|
||||
|
||||
BufferId buffer_a;
|
||||
@@ -214,11 +226,19 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
} while (channel_state->has_deleted_buffers);
|
||||
auto& src_buffer = slot_buffers[buffer_a];
|
||||
auto& dest_buffer = slot_buffers[buffer_b];
|
||||
SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
|
||||
SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
|
||||
if (!src_window) {
|
||||
SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
|
||||
}
|
||||
if (!dst_window) {
|
||||
SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
|
||||
}
|
||||
const u64 src_copy_offset =
|
||||
src_window ? u64{src_window->offset} : u64{src_buffer.Offset(*cpu_src_address)};
|
||||
const u64 dst_copy_offset =
|
||||
dst_window ? u64{dst_window->offset} : u64{dest_buffer.Offset(*cpu_dest_address)};
|
||||
std::array copies{BufferCopy{
|
||||
.src_offset = src_buffer.Offset(*cpu_src_address),
|
||||
.dst_offset = dest_buffer.Offset(*cpu_dest_address),
|
||||
.src_offset = src_copy_offset,
|
||||
.dst_offset = dst_copy_offset,
|
||||
.size = amount,
|
||||
}};
|
||||
|
||||
@@ -230,7 +250,9 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
tmp_intervals.push_back({new_base_address, size});
|
||||
uncommitted_gpu_modified_ranges.Add(new_base_address, size);
|
||||
};
|
||||
gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror);
|
||||
if (!dst_window) {
|
||||
gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror);
|
||||
}
|
||||
// This subtraction in this order is important for overlapping copies.
|
||||
ForgetGpuModifiedRange(*cpu_dest_address, amount);
|
||||
const bool has_new_downloads = tmp_intervals.size() != 0;
|
||||
@@ -238,10 +260,25 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
gpu_modified_ranges.Add(pair.first, pair.second);
|
||||
}
|
||||
const auto& copy = copies[0];
|
||||
src_buffer.MarkUsage(copy.src_offset, copy.size);
|
||||
dest_buffer.MarkUsage(copy.dst_offset, copy.size);
|
||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||
if (has_new_downloads) {
|
||||
if (!src_window) {
|
||||
src_buffer.MarkUsage(copy.src_offset, copy.size);
|
||||
}
|
||||
if (!dst_window) {
|
||||
dest_buffer.MarkUsage(copy.dst_offset, copy.size);
|
||||
}
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
runtime.CopyBuffer(
|
||||
runtime.ResolveWindowHandle(dest_buffer, dst_window.has_value(),
|
||||
dst_window ? dst_window->window_index : 0),
|
||||
runtime.ResolveWindowHandle(src_buffer, src_window.has_value(),
|
||||
src_window ? src_window->window_index : 0),
|
||||
copies, true);
|
||||
} else {
|
||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||
}
|
||||
if (dst_window) {
|
||||
MarkWrittenBufferInPlace(*cpu_dest_address, static_cast<u32>(amount));
|
||||
} else if (has_new_downloads) {
|
||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||
}
|
||||
|
||||
@@ -274,6 +311,54 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
auto BufferCache<P>::TryObtainWindowBuffer([[maybe_unused]] DAddr device_addr,
|
||||
[[maybe_unused]] u32 size,
|
||||
[[maybe_unused]] u64 alignment,
|
||||
[[maybe_unused]] ObtainBufferOperation post_op)
|
||||
-> std::optional<WindowBufferRef> {
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
if (!runtime.SupportsUnifiedDirectBinding()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
u64 window_index = 0;
|
||||
u64 window_offset = 0;
|
||||
if (!ResolveUnifiedDirectBinding(device_addr, size, alignment, window_index,
|
||||
window_offset)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
switch (post_op) {
|
||||
case ObtainBufferOperation::MarkAsWritten:
|
||||
MarkWrittenBufferInPlace(device_addr, size);
|
||||
break;
|
||||
case ObtainBufferOperation::DiscardWrite: {
|
||||
const DAddr aligned_start = Common::AlignDown(device_addr, 64);
|
||||
const DAddr aligned_end = Common::AlignUp(device_addr + size, 64);
|
||||
const size_t aligned_size = aligned_end - aligned_start;
|
||||
ClearDownload(aligned_start, aligned_size);
|
||||
ForgetGpuModifiedRange(aligned_start, aligned_size);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return WindowBufferRef{window_index, static_cast<u32>(window_offset)};
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
auto BufferCache<P>::TryObtainWindowBufferGpu(GPUVAddr gpu_addr, u32 size, u64 alignment,
|
||||
ObtainBufferOperation post_op)
|
||||
-> std::optional<WindowBufferRef> {
|
||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
|
||||
if (!device_addr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return TryObtainWindowBuffer(*device_addr, size, alignment, post_op);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_addr, u32 size,
|
||||
ObtainBufferSynchronize sync_info,
|
||||
@@ -605,16 +690,24 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
struct QueuedDownload {
|
||||
BufferCopy copy;
|
||||
BufferId buffer_id;
|
||||
bool written_in_place;
|
||||
};
|
||||
|
||||
boost::container::small_vector<QueuedDownload, 16> downloads;
|
||||
boost::container::small_vector<BufferCopy, 16> in_place_downloads;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
for (const Common::RangeSet<DAddr>& range_set : committed_gpu_modified_ranges) {
|
||||
range_set.ForEach([&](DAddr interval_lower, DAddr interval_upper) {
|
||||
const std::size_t size = interval_upper - interval_lower;
|
||||
const DAddr device_addr = interval_lower;
|
||||
in_place_gpu_written_ranges.ForEachInRange(
|
||||
device_addr, size, [&](DAddr start, DAddr end) {
|
||||
in_place_downloads.push_back(BufferCopy{
|
||||
.src_offset = static_cast<size_t>(start),
|
||||
.dst_offset = 0,
|
||||
.size = end - start,
|
||||
});
|
||||
});
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||
const DAddr buffer_start = buffer.CpuAddr();
|
||||
const DAddr buffer_end = buffer_start + buffer.SizeBytes();
|
||||
@@ -624,8 +717,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
new_start, new_end - new_start, false,
|
||||
[&](u64 device_addr_out, u64 range_size) {
|
||||
const DAddr buffer_addr = buffer.CpuAddr();
|
||||
const auto add_download = [&](DAddr start, DAddr end,
|
||||
bool written_in_place) {
|
||||
const auto add_download = [&](DAddr start, DAddr end) {
|
||||
const u64 new_offset = start - buffer_addr;
|
||||
const u64 new_size = end - start;
|
||||
downloads.push_back({
|
||||
@@ -635,7 +727,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
.size = new_size,
|
||||
},
|
||||
buffer_id,
|
||||
written_in_place,
|
||||
});
|
||||
// Align up to avoid cache conflicts
|
||||
constexpr u64 align = 64ULL;
|
||||
@@ -643,29 +734,28 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
total_size_bytes += (new_size + align - 1) & mask;
|
||||
largest_copy = (std::max)(largest_copy, new_size);
|
||||
};
|
||||
const auto split_by_in_place = [&](DAddr start, DAddr end) {
|
||||
const auto skip_in_place = [&](DAddr start, DAddr end) {
|
||||
DAddr cursor = start;
|
||||
in_place_gpu_written_ranges.ForEachInRange(
|
||||
start, end - start, [&](DAddr in_start, DAddr in_end) {
|
||||
if (cursor < in_start) {
|
||||
add_download(cursor, in_start, false);
|
||||
add_download(cursor, in_start);
|
||||
}
|
||||
add_download(in_start, in_end, true);
|
||||
cursor = in_end;
|
||||
});
|
||||
if (cursor < end) {
|
||||
add_download(cursor, end, false);
|
||||
add_download(cursor, end);
|
||||
}
|
||||
};
|
||||
|
||||
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size,
|
||||
split_by_in_place);
|
||||
skip_in_place);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
committed_gpu_modified_ranges.clear();
|
||||
if (downloads.empty()) {
|
||||
if (downloads.empty() && in_place_downloads.empty()) {
|
||||
pending_downloads.emplace_back();
|
||||
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
||||
return;
|
||||
@@ -683,18 +773,13 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
boost::container::small_vector<u64, 4> window_ids;
|
||||
UnifiedWindowGroups groups;
|
||||
u64 staging_size_bytes = 0;
|
||||
bool has_in_place_writes = false;
|
||||
for (auto& [copy, buffer_id, written_in_place] : downloads) {
|
||||
for (const BufferCopy& copy : in_place_downloads) {
|
||||
async_downloads.Add(static_cast<DAddr>(copy.src_offset), copy.size);
|
||||
batch.unified_copies.push_back(copy);
|
||||
}
|
||||
for (auto& [copy, buffer_id] : downloads) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
const DAddr orig_device_addr = buffer.CpuAddr() + copy.src_offset;
|
||||
if (written_in_place) {
|
||||
BufferCopy record{copy};
|
||||
record.src_offset = static_cast<size_t>(orig_device_addr);
|
||||
async_downloads.Add(orig_device_addr, copy.size);
|
||||
batch.unified_copies.push_back(record);
|
||||
has_in_place_writes = true;
|
||||
continue;
|
||||
}
|
||||
bool unified = false;
|
||||
if constexpr (USE_UNIFIED_MEMORY) {
|
||||
if (runtime.HasUnifiedMemory()) {
|
||||
@@ -750,8 +835,8 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
}
|
||||
}
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
if (has_in_place_writes) {
|
||||
runtime.UnifiedMemoryShaderWriteBarrier();
|
||||
if (!in_place_downloads.empty()) {
|
||||
runtime.UnifiedMemoryWriteBarrier();
|
||||
}
|
||||
}
|
||||
runtime.PostCopyBarrier();
|
||||
@@ -1040,15 +1125,25 @@ void BufferCache<P>::BindHostVertexBuffers() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostDrawIndirectBuffers() {
|
||||
const auto bind_buffer = [this](const Binding& binding) {
|
||||
const auto bind_buffer = [this](const Binding& binding) -> std::optional<WindowBufferRef> {
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
if (auto window = TryObtainWindowBuffer(binding.device_addr, binding.size,
|
||||
UNIFIED_INDIRECT_BINDING_ALIGNMENT,
|
||||
ObtainBufferOperation::DoNothing)) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
|
||||
return std::nullopt;
|
||||
};
|
||||
draw_indirect_count_window.reset();
|
||||
draw_indirect_window.reset();
|
||||
if (current_draw_indirect->include_count) {
|
||||
bind_buffer(channel_state->count_buffer_binding);
|
||||
draw_indirect_count_window = bind_buffer(channel_state->count_buffer_binding);
|
||||
}
|
||||
bind_buffer(channel_state->indirect_buffer_binding);
|
||||
draw_indirect_window = bind_buffer(channel_state->indirect_buffer_binding);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1069,7 +1164,6 @@ void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind) {
|
||||
++channel_state->uniform_cache_shots[0];
|
||||
const Binding& binding = channel_state->uniform_buffers[stage][index];
|
||||
const DAddr device_addr = binding.device_addr;
|
||||
const u32 size = (std::min)(binding.size, (*channel_state->uniform_buffer_sizes)[stage][index]);
|
||||
@@ -1088,6 +1182,19 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
return alignment > 1 && (offset % alignment) != 0;
|
||||
}
|
||||
}();
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING && !NEEDS_BIND_UNIFORM_INDEX) {
|
||||
u64 window_index = 0;
|
||||
u64 window_offset = 0;
|
||||
if (runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
size <= runtime.UnifiedMaxUniformBufferRange() &&
|
||||
ResolveUnifiedDirectBinding(device_addr, size, runtime.UnifiedUniformBufferAlignment(),
|
||||
window_index, window_offset)) {
|
||||
runtime.BindBufferFromWindow(window_index, static_cast<u32>(window_offset), size);
|
||||
channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
++channel_state->uniform_cache_shots[0];
|
||||
const bool use_fast_buffer = needs_alignment_stream
|
||||
|| (has_host_buffer && size <= channel_state->uniform_buffer_skip_cache_size
|
||||
&& !memory_tracker.IsRegionGpuModified(device_addr, size));
|
||||
@@ -1159,13 +1266,15 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING && !NEEDS_BIND_STORAGE_INDEX) {
|
||||
u64 window_index = 0;
|
||||
u64 window_offset = 0;
|
||||
if (!is_written && runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
if (runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
size <= runtime.UnifiedMaxStorageBufferRange() &&
|
||||
ResolveUnifiedDirectBinding(binding.device_addr, size,
|
||||
runtime.UnifiedStorageBufferAlignment(), window_index,
|
||||
window_offset)) {
|
||||
runtime.BindStorageBufferFromWindow(window_index, static_cast<u32>(window_offset),
|
||||
size);
|
||||
if (is_written) {
|
||||
MarkWrittenBufferInPlace(binding.device_addr, size);
|
||||
}
|
||||
runtime.BindBufferFromWindow(window_index, static_cast<u32>(window_offset), size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1194,9 +1303,27 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
|
||||
const TextureBufferBinding& binding = channel_state->texture_buffers[stage][index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
const u32 size = binding.size;
|
||||
const bool is_written = ((channel_state->written_texture_buffers[stage] >> index) & 1) != 0;
|
||||
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING && !SEPARATE_IMAGE_BUFFERS_BINDINGS) {
|
||||
u64 window_index = 0;
|
||||
u64 window_offset = 0;
|
||||
if (runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
ResolveUnifiedDirectBinding(binding.device_addr, size,
|
||||
runtime.UnifiedTexelBufferAlignment(), window_index,
|
||||
window_offset) &&
|
||||
runtime.BindTextureBufferFromWindow(window_index,
|
||||
static_cast<u32>(window_offset), size,
|
||||
binding.format)) {
|
||||
if (is_written) {
|
||||
MarkWrittenBufferInPlace(binding.device_addr, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||
|
||||
const bool is_written = ((channel_state->written_texture_buffers[stage] >> index) & 1) != 0;
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||
}
|
||||
@@ -1221,17 +1348,73 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
|
||||
if (maxwell3d->regs.transform_feedback_enabled == 0) {
|
||||
return;
|
||||
}
|
||||
HostBindings<typename P::Buffer> host_bindings;
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
const auto is_active = [&](u32 index) {
|
||||
const Binding& binding = channel_state->transform_feedback_buffers[index];
|
||||
const auto& control = maxwell3d->regs.transform_feedback.controls[index];
|
||||
const bool has_layout = control.varying_count != 0 || control.stride != 0;
|
||||
return has_layout && binding.buffer_id != NULL_BUFFER_ID && binding.size != 0;
|
||||
};
|
||||
|
||||
std::array<u64, NUM_TRANSFORM_FEEDBACK_BUFFERS> window_indices{};
|
||||
std::array<u64, NUM_TRANSFORM_FEEDBACK_BUFFERS> window_offsets{};
|
||||
u32 window_mask = 0;
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
if (runtime.SupportsUnifiedTransformFeedbackBinding()) {
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
if (!is_active(index)) {
|
||||
continue;
|
||||
}
|
||||
const Binding& binding = channel_state->transform_feedback_buffers[index];
|
||||
if (binding.size > runtime.UnifiedMaxTransformFeedbackBufferSize()) {
|
||||
continue;
|
||||
}
|
||||
if (ResolveUnifiedDirectBinding(binding.device_addr, binding.size,
|
||||
UNIFIED_TRANSFORM_FEEDBACK_BINDING_ALIGNMENT,
|
||||
window_indices[index], window_offsets[index])) {
|
||||
window_mask |= 1u << index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
if (window_mask != 0) {
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
const Binding& binding = channel_state->transform_feedback_buffers[index];
|
||||
if (((window_mask >> index) & 1) != 0) {
|
||||
TouchBuffer(slot_buffers[binding.buffer_id], binding.buffer_id);
|
||||
MarkWrittenBufferInPlace(binding.device_addr, binding.size);
|
||||
runtime.BindTransformFeedbackBufferFromWindow(
|
||||
index, window_indices[index], static_cast<u32>(window_offsets[index]),
|
||||
binding.size);
|
||||
continue;
|
||||
}
|
||||
if (!is_active(index)) {
|
||||
runtime.BindNullTransformFeedbackBuffer(index);
|
||||
continue;
|
||||
}
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
buffer.MarkUsage(offset, size);
|
||||
runtime.BindTransformFeedbackBuffer(index, buffer, offset, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HostBindings<typename P::Buffer> host_bindings;
|
||||
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
|
||||
const Binding& binding = channel_state->transform_feedback_buffers[index];
|
||||
|
||||
Buffer* host_buffer = &slot_buffers[NULL_BUFFER_ID];
|
||||
u32 offset = 0;
|
||||
u32 size = 0;
|
||||
|
||||
if (has_layout && binding.buffer_id != NULL_BUFFER_ID && binding.size != 0) {
|
||||
if (is_active(index)) {
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
size = binding.size;
|
||||
@@ -1276,6 +1459,19 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
|
||||
return alignment > 1 && (offset % alignment) != 0;
|
||||
}
|
||||
}();
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING && !NEEDS_BIND_UNIFORM_INDEX) {
|
||||
u64 window_index = 0;
|
||||
u64 window_offset = 0;
|
||||
if (runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
size <= runtime.UnifiedMaxUniformBufferRange() &&
|
||||
ResolveUnifiedDirectBinding(binding.device_addr, size,
|
||||
runtime.UnifiedUniformBufferAlignment(), window_index,
|
||||
window_offset)) {
|
||||
runtime.BindBufferFromWindow(window_index, static_cast<u32>(window_offset), size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (!IS_OPENGL) {
|
||||
if (needs_alignment_stream) {
|
||||
const std::span<u8> span =
|
||||
@@ -1311,13 +1507,15 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING && !NEEDS_BIND_STORAGE_INDEX) {
|
||||
u64 window_index = 0;
|
||||
u64 window_offset = 0;
|
||||
if (!is_written && runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
if (runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
size <= runtime.UnifiedMaxStorageBufferRange() &&
|
||||
ResolveUnifiedDirectBinding(binding.device_addr, size,
|
||||
runtime.UnifiedStorageBufferAlignment(), window_index,
|
||||
window_offset)) {
|
||||
runtime.BindStorageBufferFromWindow(window_index, static_cast<u32>(window_offset),
|
||||
size);
|
||||
if (is_written) {
|
||||
MarkWrittenBufferInPlace(binding.device_addr, size);
|
||||
}
|
||||
runtime.BindBufferFromWindow(window_index, static_cast<u32>(window_offset), size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1346,10 +1544,28 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
|
||||
const TextureBufferBinding& binding = channel_state->compute_texture_buffers[index];
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
const u32 size = binding.size;
|
||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||
|
||||
const bool is_written =
|
||||
((channel_state->written_compute_texture_buffers >> index) & 1) != 0;
|
||||
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING && !SEPARATE_IMAGE_BUFFERS_BINDINGS) {
|
||||
u64 window_index = 0;
|
||||
u64 window_offset = 0;
|
||||
if (runtime.SupportsUnifiedDescriptorBinding() &&
|
||||
ResolveUnifiedDirectBinding(binding.device_addr, size,
|
||||
runtime.UnifiedTexelBufferAlignment(), window_index,
|
||||
window_offset) &&
|
||||
runtime.BindTextureBufferFromWindow(window_index,
|
||||
static_cast<u32>(window_offset), size,
|
||||
binding.format)) {
|
||||
if (is_written) {
|
||||
MarkWrittenBufferInPlace(binding.device_addr, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||
|
||||
if (is_written) {
|
||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||
}
|
||||
@@ -1608,6 +1824,12 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
if (!in_place_gpu_written_ranges.Empty() && IntersectsInPlaceWrites(device_addr, size)) {
|
||||
SynchronizeBuffer(slot_buffers[buffer_id], device_addr, size);
|
||||
in_place_gpu_written_ranges.Subtract(device_addr, size);
|
||||
}
|
||||
}
|
||||
if constexpr (!IS_OPENGL) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.setWriteTick(runtime.CurrentTick());
|
||||
@@ -1619,10 +1841,13 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::MarkWrittenBufferInPlace(DAddr device_addr, u32 size) {
|
||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
||||
gpu_modified_ranges.Add(device_addr, size);
|
||||
uncommitted_gpu_modified_ranges.Add(device_addr, size);
|
||||
in_place_gpu_written_ranges.Add(device_addr, size);
|
||||
if constexpr (!IS_OPENGL) {
|
||||
in_place_write_tick = runtime.CurrentTick();
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1631,6 +1856,31 @@ void BufferCache<P>::ForgetGpuModifiedRange(DAddr device_addr, u64 size) {
|
||||
in_place_gpu_written_ranges.Subtract(device_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::IsRegionWrittenInPlace(DAddr device_addr, u64 size) {
|
||||
u64 covered = 0;
|
||||
in_place_gpu_written_ranges.ForEachInRange(
|
||||
device_addr, size, [&](DAddr start, DAddr end) { covered += end - start; });
|
||||
return covered == size;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::IntersectsInPlaceWrites(DAddr device_addr, u64 size) {
|
||||
bool intersects = false;
|
||||
in_place_gpu_written_ranges.ForEachInRange(device_addr, size,
|
||||
[&](DAddr, DAddr) { intersects = true; });
|
||||
return intersects;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::WaitForInPlaceWrites() {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
if (in_place_write_tick > runtime.KnownGpuTick()) {
|
||||
runtime.Wait(in_place_write_tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) {
|
||||
if (device_addr == 0) {
|
||||
@@ -1858,6 +2108,17 @@ void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 larg
|
||||
if (TryUnifiedUploadMemory(buffer, copies)) {
|
||||
return;
|
||||
}
|
||||
if constexpr (USE_UNIFIED_DIRECT_BINDING) {
|
||||
if (!in_place_gpu_written_ranges.Empty()) {
|
||||
const DAddr buffer_start = buffer.CpuAddr();
|
||||
for (const BufferCopy& copy : copies) {
|
||||
if (IntersectsInPlaceWrites(buffer_start + copy.dst_offset, copy.size)) {
|
||||
WaitForInPlaceWrites();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
MappedUploadMemory(buffer, total_size_bytes, copies);
|
||||
} else {
|
||||
@@ -1939,7 +2200,8 @@ bool BufferCache<P>::ResolveUnifiedDirectBinding([[maybe_unused]] DAddr device_a
|
||||
}
|
||||
contiguous += Core::DEVICE_PAGESIZE;
|
||||
}
|
||||
if (IsRegionGpuModified(device_addr, size)) {
|
||||
if (IsRegionGpuModified(device_addr, size) &&
|
||||
!IsRegionWrittenInPlace(device_addr, size)) {
|
||||
return false;
|
||||
}
|
||||
window_index = relative / window_size;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
@@ -184,6 +185,7 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
||||
static constexpr bool USE_UNIFIED_UPLOADS = P::USE_UNIFIED_UPLOADS;
|
||||
static constexpr bool USE_UNIFIED_DIRECT_BINDING = P::USE_UNIFIED_DIRECT_BINDING;
|
||||
static constexpr u64 UNIFIED_VERTEX_BINDING_ALIGNMENT = 4;
|
||||
static constexpr u64 UNIFIED_TRANSFORM_FEEDBACK_BINDING_ALIGNMENT = 4;
|
||||
|
||||
#ifdef YUZU_LEGACY
|
||||
static constexpr s64 TARGET_THRESHOLD = 3_GiB;
|
||||
@@ -270,6 +272,20 @@ public:
|
||||
void BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, PixelFormat format,
|
||||
bool is_written, bool is_image);
|
||||
|
||||
struct WindowBufferRef {
|
||||
u64 window_index;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
static constexpr u64 UNIFIED_INDIRECT_BINDING_ALIGNMENT = 4;
|
||||
static constexpr u64 UNIFIED_COPY_BINDING_ALIGNMENT = 1;
|
||||
|
||||
[[nodiscard]] std::optional<WindowBufferRef> TryObtainWindowBuffer(
|
||||
DAddr device_addr, u32 size, u64 alignment, ObtainBufferOperation post_op);
|
||||
|
||||
[[nodiscard]] std::optional<WindowBufferRef> TryObtainWindowBufferGpu(
|
||||
GPUVAddr gpu_addr, u32 size, u64 alignment, ObtainBufferOperation post_op);
|
||||
|
||||
[[nodiscard]] std::pair<Buffer*, u32> ObtainBuffer(GPUVAddr gpu_addr, u32 size,
|
||||
ObtainBufferSynchronize sync_info,
|
||||
ObtainBufferOperation post_op);
|
||||
@@ -317,6 +333,15 @@ public:
|
||||
|
||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
|
||||
|
||||
[[nodiscard]] const std::optional<WindowBufferRef>& GetDrawIndirectWindow() const noexcept {
|
||||
return draw_indirect_window;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<WindowBufferRef>& GetDrawIndirectCountWindow()
|
||||
const noexcept {
|
||||
return draw_indirect_count_window;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void BufferOperations(Func&& func) {
|
||||
do {
|
||||
@@ -422,6 +447,12 @@ private:
|
||||
|
||||
void ForgetGpuModifiedRange(DAddr device_addr, u64 size);
|
||||
|
||||
[[nodiscard]] bool IsRegionWrittenInPlace(DAddr device_addr, u64 size);
|
||||
|
||||
[[nodiscard]] bool IntersectsInPlaceWrites(DAddr device_addr, u64 size);
|
||||
|
||||
void WaitForInPlaceWrites();
|
||||
|
||||
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size);
|
||||
|
||||
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
||||
@@ -506,6 +537,9 @@ private:
|
||||
|
||||
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
|
||||
|
||||
std::optional<WindowBufferRef> draw_indirect_window;
|
||||
std::optional<WindowBufferRef> draw_indirect_count_window;
|
||||
|
||||
u32 last_index_count = 0;
|
||||
|
||||
u32 enabled_vertex_buffers_mask = 0;
|
||||
@@ -518,6 +552,7 @@ private:
|
||||
Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
|
||||
Common::RangeSet<DAddr> gpu_modified_ranges;
|
||||
Common::RangeSet<DAddr> in_place_gpu_written_ranges;
|
||||
u64 in_place_write_tick = 0;
|
||||
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
|
||||
|
||||
// Async Buffers
|
||||
|
||||
@@ -491,17 +491,22 @@ void BufferCacheRuntime::UnifiedMemoryHostBarrier() {
|
||||
});
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::UnifiedMemoryShaderWriteBarrier() {
|
||||
static constexpr VkMemoryBarrier SHADER_WRITE_BARRIER{
|
||||
void BufferCacheRuntime::UnifiedMemoryWriteBarrier() {
|
||||
VkAccessFlags src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
VkPipelineStageFlags src_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER;
|
||||
if (device.IsExtTransformFeedbackSupported()) {
|
||||
src_access |= VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT;
|
||||
src_stages |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;
|
||||
}
|
||||
const VkMemoryBarrier write_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.srcAccessMask = src_access,
|
||||
.dstAccessMask = VK_ACCESS_HOST_READ_BIT,
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_HOST_BIT, 0,
|
||||
SHADER_WRITE_BARRIER);
|
||||
scheduler.Record([write_barrier, src_stages](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(src_stages, VK_PIPELINE_STAGE_HOST_BIT, 0, write_barrier);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -676,6 +681,55 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si
|
||||
});
|
||||
}
|
||||
|
||||
VkBufferView BufferCacheRuntime::UnifiedWindowTexelView(size_t window_index, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format) {
|
||||
const auto it{std::ranges::find_if(unified_window_views, [&](const UnifiedWindowView& view) {
|
||||
return view.window_index == window_index && view.offset == offset && view.size == size &&
|
||||
view.format == format;
|
||||
})};
|
||||
if (it != unified_window_views.end()) {
|
||||
return *it->handle;
|
||||
}
|
||||
if (unified_window_views.size() >= MAX_UNIFIED_WINDOW_VIEWS) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
const VkBuffer window_buffer = UnifiedWindowBuffer(window_index);
|
||||
if (window_buffer == VK_NULL_HANDLE) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
unified_window_views.push_back({
|
||||
.window_index = window_index,
|
||||
.offset = offset,
|
||||
.size = size,
|
||||
.format = format,
|
||||
.handle = device.GetLogical().CreateBufferView({
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.buffer = window_buffer,
|
||||
.format = TexelBufferFormat(format),
|
||||
.offset = offset,
|
||||
.range = size,
|
||||
}),
|
||||
});
|
||||
return *unified_window_views.back().handle;
|
||||
}
|
||||
|
||||
bool BufferCacheRuntime::BindTextureBufferFromWindow(size_t window_index, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format) {
|
||||
if (guest_descriptor_queue.UsesDescriptorBuffer()) {
|
||||
guest_descriptor_queue.AddTexelBuffer(VK_NULL_HANDLE, UnifiedWindowAddress(window_index),
|
||||
offset, size, TexelBufferFormat(format));
|
||||
return true;
|
||||
}
|
||||
const VkBufferView view = UnifiedWindowTexelView(window_index, offset, size, format);
|
||||
if (view == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
guest_descriptor_queue.AddTexelBuffer(view);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BufferCacheRuntime::CanBindIndexBufferDirectly(PrimitiveTopology topology,
|
||||
IndexFormat index_format) const {
|
||||
if (topology == PrimitiveTopology::Quads || topology == PrimitiveTopology::QuadStrip) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||
#include "video_core/buffer_cache/memory_tracker_base.h"
|
||||
@@ -133,8 +134,20 @@ public:
|
||||
[[nodiscard]] bool CanBindIndexBufferDirectly(PrimitiveTopology topology,
|
||||
IndexFormat index_format) const;
|
||||
|
||||
[[nodiscard]] VkDeviceAddress UnifiedWindowAddress(size_t window_index) const noexcept {
|
||||
if (!unified_memory || !unified_memory->SupportsDirectAddresses() ||
|
||||
window_index >= unified_memory->GetWindowCount()) {
|
||||
return 0;
|
||||
}
|
||||
return unified_memory->GetWindowAddress(window_index);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsUnifiedDescriptorBinding() const noexcept {
|
||||
return SupportsUnifiedDirectBinding() && !guest_descriptor_queue.UsesDescriptorBuffer();
|
||||
if (!SupportsUnifiedDirectBinding()) {
|
||||
return false;
|
||||
}
|
||||
return !guest_descriptor_queue.UsesDescriptorBuffer() ||
|
||||
unified_memory->SupportsDirectAddresses();
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 UnifiedStorageBufferAlignment() const noexcept {
|
||||
@@ -145,10 +158,48 @@ public:
|
||||
return device.GetMaxStorageBufferRange();
|
||||
}
|
||||
|
||||
void BindStorageBufferFromWindow(size_t window_index, u32 offset, u32 size) {
|
||||
guest_descriptor_queue.AddBuffer(UnifiedWindowBuffer(window_index), offset, size);
|
||||
[[nodiscard]] u64 UnifiedUniformBufferAlignment() const noexcept {
|
||||
return device.GetUniformBufferAlignment();
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 UnifiedMaxUniformBufferRange() const noexcept {
|
||||
return device.GetMaxUniformBufferRange();
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 UnifiedTexelBufferAlignment() const noexcept {
|
||||
return device.GetTexelBufferAlignment();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsUnifiedTransformFeedbackBinding() const noexcept {
|
||||
return SupportsUnifiedDirectBinding() && device.IsExtTransformFeedbackSupported();
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 UnifiedMaxTransformFeedbackBufferSize() const noexcept {
|
||||
return device.GetMaxTransformFeedbackBufferSize();
|
||||
}
|
||||
|
||||
void BindTransformFeedbackBufferFromWindow(u32 index, size_t window_index, u32 offset,
|
||||
u32 size) {
|
||||
BindTransformFeedbackBuffer(index, UnifiedWindowBuffer(window_index), offset, size);
|
||||
}
|
||||
|
||||
void BindNullTransformFeedbackBuffer(u32 index) {
|
||||
BindTransformFeedbackBuffer(index, VK_NULL_HANDLE, 0, 0);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkBuffer ResolveWindowHandle(const Buffer& buffer, bool from_window,
|
||||
size_t window_index) const noexcept {
|
||||
return from_window ? UnifiedWindowBuffer(window_index) : buffer.Handle();
|
||||
}
|
||||
|
||||
void BindBufferFromWindow(size_t window_index, u32 offset, u32 size) {
|
||||
guest_descriptor_queue.AddBuffer(UnifiedWindowBuffer(window_index),
|
||||
UnifiedWindowAddress(window_index), offset, size);
|
||||
}
|
||||
|
||||
bool BindTextureBufferFromWindow(size_t window_index, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format);
|
||||
|
||||
void CopyToUnifiedMemory(size_t window_index, VkBuffer src_buffer,
|
||||
std::span<const VideoCommon::BufferCopy> copies);
|
||||
|
||||
@@ -159,7 +210,7 @@ public:
|
||||
|
||||
void UnifiedMemoryUploadBarrier();
|
||||
|
||||
void UnifiedMemoryShaderWriteBarrier();
|
||||
void UnifiedMemoryWriteBarrier();
|
||||
|
||||
u64 CurrentTick();
|
||||
|
||||
@@ -257,6 +308,19 @@ private:
|
||||
|
||||
void AcquireUnifiedWindowsFromForeign();
|
||||
|
||||
static constexpr size_t MAX_UNIFIED_WINDOW_VIEWS = 256;
|
||||
|
||||
struct UnifiedWindowView {
|
||||
size_t window_index;
|
||||
u32 offset;
|
||||
u32 size;
|
||||
VideoCore::Surface::PixelFormat format;
|
||||
vk::BufferView handle;
|
||||
};
|
||||
|
||||
[[nodiscard]] VkBufferView UnifiedWindowTexelView(size_t window_index, u32 offset, u32 size,
|
||||
VideoCore::Surface::PixelFormat format);
|
||||
|
||||
void ReserveNullBuffer();
|
||||
vk::Buffer CreateNullBuffer();
|
||||
|
||||
@@ -271,6 +335,7 @@ private:
|
||||
|
||||
vk::Buffer null_buffer;
|
||||
std::unique_ptr<HostMemoryImport> unified_memory;
|
||||
std::vector<UnifiedWindowView> unified_window_views;
|
||||
|
||||
std::unique_ptr<Uint8Pass> uint8_pass;
|
||||
QuadIndexedPass quad_index_pass;
|
||||
|
||||
@@ -1412,10 +1412,16 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo
|
||||
std::scoped_lock lk(impl->buffer_cache.mutex);
|
||||
static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
|
||||
const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing;
|
||||
const auto [buffer, offset] =
|
||||
impl->buffer_cache.ObtainCPUBuffer(object.address, 8, sync_info, post_op);
|
||||
impl->hcr_buffer = buffer->Handle();
|
||||
impl->hcr_offset = offset;
|
||||
if (const auto window = impl->buffer_cache.TryObtainWindowBuffer(
|
||||
object.address, 8, Vulkan::BufferCache::UNIFIED_INDIRECT_BINDING_ALIGNMENT, post_op)) {
|
||||
impl->hcr_buffer = impl->buffer_cache.runtime.UnifiedWindowBuffer(window->window_index);
|
||||
impl->hcr_offset = window->offset;
|
||||
} else {
|
||||
const auto [buffer, offset] =
|
||||
impl->buffer_cache.ObtainCPUBuffer(object.address, 8, sync_info, post_op);
|
||||
impl->hcr_buffer = buffer->Handle();
|
||||
impl->hcr_offset = offset;
|
||||
}
|
||||
}
|
||||
if (impl->hcr_is_set) {
|
||||
if (impl->hcr_setup.buffer == impl->hcr_buffer &&
|
||||
@@ -1446,10 +1452,17 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo
|
||||
std::scoped_lock lk(impl->buffer_cache.mutex);
|
||||
const auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
|
||||
const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing;
|
||||
const auto [buffer, offset] =
|
||||
impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op);
|
||||
to_resolve = buffer->Handle();
|
||||
to_resolve_offset = static_cast<u32>(offset);
|
||||
if (const auto window = impl->buffer_cache.TryObtainWindowBuffer(
|
||||
address, resolve_size,
|
||||
impl->buffer_cache.runtime.UnifiedStorageBufferAlignment(), post_op)) {
|
||||
to_resolve = impl->buffer_cache.runtime.UnifiedWindowBuffer(window->window_index);
|
||||
to_resolve_offset = window->offset;
|
||||
} else {
|
||||
const auto [buffer, offset] =
|
||||
impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op);
|
||||
to_resolve = buffer->Handle();
|
||||
to_resolve_offset = static_cast<u32>(offset);
|
||||
}
|
||||
}
|
||||
bool was_running = impl->is_hcr_running;
|
||||
if (was_running) {
|
||||
|
||||
@@ -303,11 +303,18 @@ void RasterizerVulkan::DrawIndirect() {
|
||||
const auto& params = maxwell3d->draw_manager.indirect_state;
|
||||
buffer_cache.SetDrawIndirect(¶ms);
|
||||
PrepareDraw(params.is_indexed, [this, ¶ms] {
|
||||
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
|
||||
const auto& buffer = indirect_buffer.first;
|
||||
const auto& offset = indirect_buffer.second;
|
||||
VkBuffer buffer_handle;
|
||||
u32 offset;
|
||||
if (const auto& window = buffer_cache.GetDrawIndirectWindow()) {
|
||||
buffer_handle = buffer_cache.runtime.UnifiedWindowBuffer(window->window_index);
|
||||
offset = window->offset;
|
||||
} else {
|
||||
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
|
||||
buffer_handle = indirect_buffer.first->Handle();
|
||||
offset = indirect_buffer.second;
|
||||
}
|
||||
if (params.is_byte_count) {
|
||||
scheduler.Record([buffer_obj = buffer->Handle(), offset,
|
||||
scheduler.Record([buffer_obj = buffer_handle, offset,
|
||||
stride = params.stride](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.DrawIndirectByteCountEXT(1, 0, buffer_obj, offset, 0,
|
||||
static_cast<u32>(stride));
|
||||
@@ -315,11 +322,19 @@ void RasterizerVulkan::DrawIndirect() {
|
||||
return;
|
||||
}
|
||||
if (params.include_count) {
|
||||
const auto count = buffer_cache.GetDrawIndirectCount();
|
||||
const auto& draw_buffer = count.first;
|
||||
const auto& offset_base = count.second;
|
||||
scheduler.Record([draw_buffer_obj = draw_buffer->Handle(),
|
||||
buffer_obj = buffer->Handle(), offset_base, offset,
|
||||
VkBuffer draw_buffer_handle;
|
||||
u32 offset_base;
|
||||
if (const auto& count_window = buffer_cache.GetDrawIndirectCountWindow()) {
|
||||
draw_buffer_handle =
|
||||
buffer_cache.runtime.UnifiedWindowBuffer(count_window->window_index);
|
||||
offset_base = count_window->offset;
|
||||
} else {
|
||||
const auto count = buffer_cache.GetDrawIndirectCount();
|
||||
draw_buffer_handle = count.first->Handle();
|
||||
offset_base = count.second;
|
||||
}
|
||||
scheduler.Record([draw_buffer_obj = draw_buffer_handle,
|
||||
buffer_obj = buffer_handle, offset_base, offset,
|
||||
params](vk::CommandBuffer cmdbuf) {
|
||||
if (params.is_indexed) {
|
||||
cmdbuf.DrawIndexedIndirectCount(
|
||||
@@ -333,7 +348,7 @@ void RasterizerVulkan::DrawIndirect() {
|
||||
});
|
||||
return;
|
||||
}
|
||||
scheduler.Record([buffer_obj = buffer->Handle(), offset, params](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([buffer_obj = buffer_handle, offset, params](vk::CommandBuffer cmdbuf) {
|
||||
if (params.is_indexed) {
|
||||
cmdbuf.DrawIndexedIndirect(buffer_obj, offset,
|
||||
static_cast<u32>(params.max_draw_counts),
|
||||
@@ -605,11 +620,21 @@ void RasterizerVulkan::DispatchCompute() {
|
||||
// DispatchIndirect
|
||||
static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
|
||||
const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite;
|
||||
const auto [buffer, offset] =
|
||||
buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op);
|
||||
VkBuffer indirect_handle;
|
||||
u32 indirect_base;
|
||||
if (const auto window = buffer_cache.TryObtainWindowBufferGpu(
|
||||
*indirect_address, 12, BufferCache::UNIFIED_INDIRECT_BINDING_ALIGNMENT, post_op)) {
|
||||
indirect_handle = buffer_cache.runtime.UnifiedWindowBuffer(window->window_index);
|
||||
indirect_base = window->offset;
|
||||
} else {
|
||||
const auto [buffer, offset] =
|
||||
buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op);
|
||||
indirect_handle = buffer->Handle();
|
||||
indirect_base = offset;
|
||||
}
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([pipeline, indirect_buffer = buffer->Handle(),
|
||||
indirect_offset = offset](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([pipeline, indirect_buffer = indirect_handle,
|
||||
indirect_offset = indirect_base](vk::CommandBuffer cmdbuf) {
|
||||
if (!pipeline->IsBound()) {
|
||||
return;
|
||||
}
|
||||
@@ -1030,8 +1055,19 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
|
||||
static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
|
||||
const auto post_op = IS_IMAGE_UPLOAD ? VideoCommon::ObtainBufferOperation::DoNothing
|
||||
: VideoCommon::ObtainBufferOperation::MarkAsWritten;
|
||||
const auto [buffer, offset] =
|
||||
buffer_cache.ObtainBuffer(buffer_operand.address, buffer_size, sync_info, post_op);
|
||||
VkBuffer buffer_handle;
|
||||
u32 offset;
|
||||
if (const auto window = buffer_cache.TryObtainWindowBufferGpu(
|
||||
buffer_operand.address, buffer_size,
|
||||
BufferCache::UNIFIED_INDIRECT_BINDING_ALIGNMENT, post_op)) {
|
||||
buffer_handle = buffer_cache.runtime.UnifiedWindowBuffer(window->window_index);
|
||||
offset = window->offset;
|
||||
} else {
|
||||
const auto [buffer, buffer_offset] =
|
||||
buffer_cache.ObtainBuffer(buffer_operand.address, buffer_size, sync_info, post_op);
|
||||
buffer_handle = buffer->Handle();
|
||||
offset = buffer_offset;
|
||||
}
|
||||
|
||||
const auto [image, copy] = texture_cache.DmaBufferImageCopy(
|
||||
copy_info, buffer_operand, image_operand, image_id, IS_IMAGE_UPLOAD);
|
||||
@@ -1039,12 +1075,12 @@ bool AccelerateDMA::DmaBufferImageCopy(const Tegra::DMA::ImageCopy& copy_info,
|
||||
|
||||
if constexpr (IS_IMAGE_UPLOAD) {
|
||||
texture_cache.PrepareImage(image_id, true, false);
|
||||
image->UploadMemory(buffer->Handle(), offset, copy_span);
|
||||
image->UploadMemory(buffer_handle, offset, copy_span);
|
||||
} else {
|
||||
if (offset % BytesPerBlock(image->info.format)) {
|
||||
return false;
|
||||
}
|
||||
texture_cache.DownloadImageIntoBuffer(image, buffer->Handle(), offset, copy_span,
|
||||
texture_cache.DownloadImageIntoBuffer(image, buffer_handle, offset, copy_span,
|
||||
buffer_operand.address, buffer_size);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -347,6 +347,22 @@ public:
|
||||
return properties.properties.limits.maxStorageBufferRange;
|
||||
}
|
||||
|
||||
/// Returns the maximum range for uniform buffers.
|
||||
VkDeviceSize GetMaxUniformBufferRange() const {
|
||||
return properties.properties.limits.maxUniformBufferRange;
|
||||
}
|
||||
|
||||
/// Returns transform feedback buffer alignment requirement.
|
||||
VkDeviceSize GetMaxTransformFeedbackBufferSize() const {
|
||||
return properties.transform_feedback.maxTransformFeedbackBufferSize;
|
||||
}
|
||||
|
||||
// Returns texel buffer alignment requirement.
|
||||
VkDeviceSize GetTexelBufferAlignment() const {
|
||||
return properties.properties.limits.minTexelBufferOffsetAlignment;
|
||||
}
|
||||
|
||||
/// Returns the maximum number of workgroups that can be dispatched in a single dimension.
|
||||
std::array<u32, 3> GetMaxComputeWorkGroupCount() const {
|
||||
const auto& count = properties.properties.limits.maxComputeWorkGroupCount;
|
||||
return {count[0], count[1], count[2]};
|
||||
|
||||
@@ -72,6 +72,11 @@ namespace Vulkan {
|
||||
return flags;
|
||||
}
|
||||
|
||||
[[nodiscard]] VkBufferUsageFlags ImportedDescriptorAddressUsage(const Device &device) {
|
||||
return ImportedDescriptorUsage(device) |
|
||||
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
||||
}
|
||||
|
||||
// Helpers translating MemoryUsage to flags/usage
|
||||
|
||||
[[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
|
||||
@@ -297,7 +302,16 @@ namespace Vulkan {
|
||||
bool HostMemoryImport::CreateDescriptorCapableWindowBuffer(
|
||||
const void *external_info, VkDeviceSize length, u32 external_type_bits,
|
||||
VkDeviceSize capacity, VkBuffer *out_buffer, u32 *out_type_mask,
|
||||
bool *out_descriptor_capable) const {
|
||||
bool *out_descriptor_capable, bool *out_address_capable, bool allow_address) const {
|
||||
if (allow_address && device.IsBufferDeviceAddressSupported() &&
|
||||
TryCreateWindowBuffer(external_info, length, external_type_bits, capacity,
|
||||
ImportedDescriptorAddressUsage(device), out_buffer,
|
||||
out_type_mask)) {
|
||||
*out_descriptor_capable = true;
|
||||
*out_address_capable = true;
|
||||
return true;
|
||||
}
|
||||
*out_address_capable = false;
|
||||
if (TryCreateWindowBuffer(external_info, length, external_type_bits, capacity,
|
||||
ImportedDescriptorUsage(device), out_buffer, out_type_mask)) {
|
||||
*out_descriptor_capable = true;
|
||||
@@ -443,6 +457,7 @@ namespace Vulkan {
|
||||
window_size = hardware_buffer_window;
|
||||
base_offset = hardware_buffer_base;
|
||||
bool every_window_descriptor_capable = true;
|
||||
bool every_window_address_capable = true;
|
||||
for (size_t i = 0; i < hardware_buffers.size(); ++i) {
|
||||
const size_t offset = hardware_buffer_base + i * hardware_buffer_window;
|
||||
if (offset >= size) {
|
||||
@@ -468,54 +483,81 @@ namespace Vulkan {
|
||||
.handleTypes =
|
||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
||||
};
|
||||
VkBuffer new_buffer{};
|
||||
u32 type_mask = 0;
|
||||
Window window{};
|
||||
bool descriptor_capable = false;
|
||||
if (!CreateDescriptorCapableWindowBuffer(&external_info, window_len,
|
||||
ahb_props.memoryTypeBits,
|
||||
ahb_props.allocationSize, &new_buffer,
|
||||
&type_mask, &descriptor_capable)) {
|
||||
break;
|
||||
}
|
||||
const auto type_index = FindImportMemoryType(memory_props, type_mask);
|
||||
if (!type_index) {
|
||||
logical.DestroyBufferRaw(new_buffer);
|
||||
break;
|
||||
}
|
||||
const VkImportAndroidHardwareBufferInfoANDROID import_info{
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.buffer = hardware_buffers[i],
|
||||
bool address_capable = false;
|
||||
const auto build_window = [&](bool allow_address) {
|
||||
VkBuffer new_buffer{};
|
||||
u32 type_mask = 0;
|
||||
if (!CreateDescriptorCapableWindowBuffer(&external_info, window_len,
|
||||
ahb_props.memoryTypeBits,
|
||||
ahb_props.allocationSize, &new_buffer,
|
||||
&type_mask, &descriptor_capable,
|
||||
&address_capable, allow_address)) {
|
||||
return false;
|
||||
}
|
||||
const auto type_index = FindImportMemoryType(memory_props, type_mask);
|
||||
if (!type_index) {
|
||||
logical.DestroyBufferRaw(new_buffer);
|
||||
return false;
|
||||
}
|
||||
const VkImportAndroidHardwareBufferInfoANDROID import_info{
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.buffer = hardware_buffers[i],
|
||||
};
|
||||
const VkMemoryDedicatedAllocateInfo dedicated_info{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||
.pNext = &import_info,
|
||||
.image = VK_NULL_HANDLE,
|
||||
.buffer = new_buffer,
|
||||
};
|
||||
const VkMemoryAllocateFlagsInfo flags_info{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
|
||||
.pNext = &dedicated_info,
|
||||
.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT,
|
||||
.deviceMask = 0,
|
||||
};
|
||||
const VkMemoryAllocateInfo alloc_info{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = address_capable ? static_cast<const void *>(&flags_info)
|
||||
: static_cast<const void *>(&dedicated_info),
|
||||
.allocationSize = ahb_props.allocationSize,
|
||||
.memoryTypeIndex = *type_index,
|
||||
};
|
||||
vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info);
|
||||
if (!memory) {
|
||||
logical.DestroyBufferRaw(new_buffer);
|
||||
return false;
|
||||
}
|
||||
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
|
||||
logical.DestroyBufferRaw(new_buffer);
|
||||
return false;
|
||||
}
|
||||
VkDeviceAddress address = 0;
|
||||
if (address_capable) {
|
||||
address = logical.GetBufferDeviceAddress(new_buffer);
|
||||
if (address == 0) {
|
||||
address_capable = false;
|
||||
}
|
||||
}
|
||||
window = Window{
|
||||
.memory = std::move(memory),
|
||||
.buffer = new_buffer,
|
||||
.address = address,
|
||||
};
|
||||
return true;
|
||||
};
|
||||
const VkMemoryDedicatedAllocateInfo dedicated_info{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||
.pNext = &import_info,
|
||||
.image = VK_NULL_HANDLE,
|
||||
.buffer = new_buffer,
|
||||
};
|
||||
const VkMemoryAllocateInfo alloc_info{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &dedicated_info,
|
||||
.allocationSize = ahb_props.allocationSize,
|
||||
.memoryTypeIndex = *type_index,
|
||||
};
|
||||
vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info);
|
||||
if (!memory) {
|
||||
logical.DestroyBufferRaw(new_buffer);
|
||||
if (!build_window(true) && !build_window(false)) {
|
||||
break;
|
||||
}
|
||||
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
|
||||
logical.DestroyBufferRaw(new_buffer);
|
||||
break;
|
||||
}
|
||||
windows.push_back(Window{
|
||||
.memory = std::move(memory),
|
||||
.buffer = new_buffer,
|
||||
});
|
||||
windows.push_back(std::move(window));
|
||||
every_window_descriptor_capable &= descriptor_capable;
|
||||
every_window_address_capable &= address_capable;
|
||||
imported_size += static_cast<size_t>(window_len);
|
||||
}
|
||||
direct_descriptors = !windows.empty() && every_window_descriptor_capable;
|
||||
direct_addresses = direct_descriptors && every_window_address_capable;
|
||||
if (windows.empty()) {
|
||||
window_size = 0;
|
||||
base_offset = 0;
|
||||
|
||||
@@ -130,10 +130,19 @@ namespace Vulkan {
|
||||
return direct_descriptors;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsDirectAddresses() const noexcept {
|
||||
return direct_addresses;
|
||||
}
|
||||
|
||||
[[nodiscard]] VkDeviceAddress GetWindowAddress(size_t index) const noexcept {
|
||||
return windows[index].address;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Window {
|
||||
vk::DeviceMemory memory;
|
||||
VkBuffer buffer{};
|
||||
VkDeviceAddress address{};
|
||||
};
|
||||
|
||||
bool ImportHostPointer(void *base, size_t size);
|
||||
@@ -150,7 +159,9 @@ namespace Vulkan {
|
||||
bool CreateDescriptorCapableWindowBuffer(const void *external_info, VkDeviceSize length,
|
||||
u32 external_type_bits, VkDeviceSize capacity,
|
||||
VkBuffer *out_buffer, u32 *out_type_mask,
|
||||
bool *out_descriptor_capable) const;
|
||||
bool *out_descriptor_capable,
|
||||
bool *out_address_capable,
|
||||
bool allow_address) const;
|
||||
|
||||
const Device &device;
|
||||
std::vector<Window> windows;
|
||||
@@ -159,6 +170,7 @@ namespace Vulkan {
|
||||
size_t base_offset{};
|
||||
bool foreign_ownership{};
|
||||
bool direct_descriptors{};
|
||||
bool direct_addresses{};
|
||||
};
|
||||
|
||||
/// Memory allocator container.
|
||||
|
||||
Reference in New Issue
Block a user