early-access version 1845
This commit is contained in:
parent
23310c6446
commit
1c8c1ad39f
14 changed files with 97 additions and 152 deletions
|
@ -1,7 +1,7 @@
|
|||
yuzu emulator early access
|
||||
=============
|
||||
|
||||
This is the source code for early-access 1844.
|
||||
This is the source code for early-access 1845.
|
||||
|
||||
## Legal Notice
|
||||
|
||||
|
|
|
@ -536,7 +536,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
|
|||
REQUIRE(rasterizer.Count() == 63);
|
||||
buffer.MarkRegionAsGpuModified(c + PAGE, PAGE);
|
||||
int num = 0;
|
||||
buffer.ForEachDownloadRange(c, WORD, true, [&](u64 offset, u64 size) { ++num; });
|
||||
buffer.ForEachDownloadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
|
||||
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
|
||||
REQUIRE(num == 0);
|
||||
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
|
||||
|
|
|
@ -226,19 +226,19 @@ public:
|
|||
/// Call 'func' for each CPU modified range and unmark those pages as CPU modified
|
||||
template <typename Func>
|
||||
void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) {
|
||||
ForEachModifiedRange<Type::CPU>(query_cpu_range, size, true, func);
|
||||
ForEachModifiedRange<Type::CPU>(query_cpu_range, size, func);
|
||||
}
|
||||
|
||||
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
|
||||
template <typename Func>
|
||||
void ForEachDownloadRange(VAddr query_cpu_range, u64 size, bool clear, Func&& func) {
|
||||
ForEachModifiedRange<Type::GPU>(query_cpu_range, size, clear, func);
|
||||
void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) {
|
||||
ForEachModifiedRange<Type::GPU>(query_cpu_range, size, func);
|
||||
}
|
||||
|
||||
/// Call 'func' for each GPU modified range and unmark those pages as GPU modified
|
||||
template <typename Func>
|
||||
void ForEachDownloadRange(Func&& func) {
|
||||
ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func);
|
||||
ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func);
|
||||
}
|
||||
|
||||
/// Mark buffer as picked
|
||||
|
@ -415,7 +415,7 @@ private:
|
|||
* @param func Function to call for each turned off region
|
||||
*/
|
||||
template <Type type, typename Func>
|
||||
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) {
|
||||
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) {
|
||||
static_assert(type != Type::Untracked);
|
||||
|
||||
const s64 difference = query_cpu_range - cpu_addr;
|
||||
|
@ -467,9 +467,7 @@ private:
|
|||
bits = (bits << left_offset) >> left_offset;
|
||||
|
||||
const u64 current_word = state_words[word_index] & bits;
|
||||
if (clear) {
|
||||
state_words[word_index] &= ~bits;
|
||||
}
|
||||
state_words[word_index] &= ~bits;
|
||||
|
||||
if constexpr (type == Type::CPU) {
|
||||
const u64 current_bits = untracked_words[word_index] & bits;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <vector>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
|
@ -78,9 +77,6 @@ class BufferCache {
|
|||
using Runtime = typename P::Runtime;
|
||||
using Buffer = typename P::Buffer;
|
||||
|
||||
using IntervalSet = boost::icl::interval_set<VAddr>;
|
||||
using IntervalType = typename IntervalSet::interval_type;
|
||||
|
||||
struct Empty {};
|
||||
|
||||
struct OverlapResult {
|
||||
|
@ -157,7 +153,6 @@ public:
|
|||
|
||||
/// Commit asynchronous downloads
|
||||
void CommitAsyncFlushes();
|
||||
void CommitAsyncFlushesHigh();
|
||||
|
||||
/// Pop asynchronous downloads
|
||||
void PopAsyncFlushes();
|
||||
|
@ -165,9 +160,6 @@ public:
|
|||
/// Return true when a CPU region is modified from the GPU
|
||||
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
|
||||
|
||||
/// Return true when a CPU region is modified from the GPU
|
||||
[[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size);
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
private:
|
||||
|
@ -280,6 +272,8 @@ private:
|
|||
|
||||
void DeleteBuffer(BufferId buffer_id);
|
||||
|
||||
void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id);
|
||||
|
||||
void NotifyBufferDeletion();
|
||||
|
||||
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const;
|
||||
|
@ -333,7 +327,9 @@ private:
|
|||
|
||||
std::vector<BufferId> cached_write_buffer_ids;
|
||||
|
||||
IntervalSet uncommitted_ranges;
|
||||
// TODO: This data structure is not optimal and it should be reworked
|
||||
std::vector<BufferId> uncommitted_downloads;
|
||||
std::deque<std::vector<BufferId>> committed_downloads;
|
||||
|
||||
size_t immediate_buffer_capacity = 0;
|
||||
std::unique_ptr<u8[]> immediate_buffer_alloc;
|
||||
|
@ -551,18 +547,29 @@ void BufferCache<P>::FlushCachedWrites() {
|
|||
|
||||
template <class P>
|
||||
bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
|
||||
return !uncommitted_ranges.empty();
|
||||
return !uncommitted_downloads.empty();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
||||
return false;
|
||||
return !committed_downloads.empty() && !committed_downloads.front().empty();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CommitAsyncFlushesHigh() {
|
||||
const IntervalSet& intervals = uncommitted_ranges;
|
||||
if (intervals.empty()) {
|
||||
void BufferCache<P>::CommitAsyncFlushes() {
|
||||
// This is intentionally passing the value by copy
|
||||
committed_downloads.push_front(uncommitted_downloads);
|
||||
uncommitted_downloads.clear();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::PopAsyncFlushes() {
|
||||
if (committed_downloads.empty()) {
|
||||
return;
|
||||
}
|
||||
auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); });
|
||||
const std::span<const BufferId> download_ids = committed_downloads.back();
|
||||
if (download_ids.empty()) {
|
||||
return;
|
||||
}
|
||||
MICROPROFILE_SCOPE(GPU_DownloadMemory);
|
||||
|
@ -570,35 +577,18 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
|||
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
for (auto& interval : intervals) {
|
||||
const std::size_t size = interval.upper() - interval.lower();
|
||||
const VAddr cpu_addr = interval.lower();
|
||||
const VAddr cpu_addr_end = interval.upper();
|
||||
ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||
boost::container::small_vector<BufferCopy, 1> copies;
|
||||
buffer.ForEachDownloadRange(
|
||||
cpu_addr, size, false, [&](u64 range_offset, u64 range_size) {
|
||||
VAddr cpu_addr_base = buffer.CpuAddr() + range_offset;
|
||||
VAddr cpu_addr_end2 = cpu_addr_base + range_size;
|
||||
const s64 difference = s64(cpu_addr_end2 - cpu_addr_end);
|
||||
cpu_addr_end2 -= u64(std::max<s64>(difference, 0));
|
||||
const s64 difference2 = s64(cpu_addr - cpu_addr_base);
|
||||
cpu_addr_base += u64(std::max<s64>(difference2, 0));
|
||||
const u64 new_size = cpu_addr_end2 - cpu_addr_base;
|
||||
const u64 new_offset = cpu_addr_base - buffer.CpuAddr();
|
||||
ASSERT(!IsRegionCpuModified(cpu_addr_base, new_size));
|
||||
downloads.push_back({
|
||||
BufferCopy{
|
||||
.src_offset = new_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = new_size,
|
||||
},
|
||||
buffer_id,
|
||||
});
|
||||
total_size_bytes += new_size;
|
||||
buffer.UnmarkRegionAsGpuModified(cpu_addr_base, new_size);
|
||||
largest_copy = std::max(largest_copy, new_size);
|
||||
});
|
||||
for (const BufferId buffer_id : download_ids) {
|
||||
slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) {
|
||||
downloads.push_back({
|
||||
BufferCopy{
|
||||
.src_offset = range_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = range_size,
|
||||
},
|
||||
buffer_id,
|
||||
});
|
||||
total_size_bytes += range_size;
|
||||
largest_copy = std::max(largest_copy, range_size);
|
||||
});
|
||||
}
|
||||
if (downloads.empty()) {
|
||||
|
@ -632,18 +622,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
|||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CommitAsyncFlushes() {
|
||||
if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) {
|
||||
CommitAsyncFlushesHigh();
|
||||
} else {
|
||||
uncommitted_ranges.clear();
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::PopAsyncFlushes() {}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
||||
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
||||
|
@ -663,25 +641,6 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
|
|||
return false;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
|
||||
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
|
||||
for (u64 page = addr >> PAGE_BITS; page < page_end;) {
|
||||
const BufferId image_id = page_table[page];
|
||||
if (!image_id) {
|
||||
++page;
|
||||
continue;
|
||||
}
|
||||
Buffer& buffer = slot_buffers[image_id];
|
||||
if (buffer.IsRegionCpuModified(addr, size)) {
|
||||
return true;
|
||||
}
|
||||
const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes();
|
||||
page = Common::DivCeil(end_addr, PAGE_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostIndexBuffer() {
|
||||
Buffer& buffer = slot_buffers[index_buffer.buffer_id];
|
||||
|
@ -1051,14 +1010,16 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s
|
|||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.MarkRegionAsGpuModified(cpu_addr, size);
|
||||
|
||||
const bool is_accuracy_high =
|
||||
Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High;
|
||||
const bool is_accuracy_high = Settings::IsGPULevelHigh();
|
||||
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
if (!is_async && !is_accuracy_high) {
|
||||
if (!is_accuracy_high || !is_async) {
|
||||
return;
|
||||
}
|
||||
const IntervalType base_interval{cpu_addr, cpu_addr + size};
|
||||
uncommitted_ranges.add(base_interval);
|
||||
if (std::ranges::find(uncommitted_downloads, buffer_id) != uncommitted_downloads.end()) {
|
||||
// Already inserted
|
||||
return;
|
||||
}
|
||||
uncommitted_downloads.push_back(buffer_id);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
@ -1142,6 +1103,7 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
|
|||
if (!copies.empty()) {
|
||||
runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies);
|
||||
}
|
||||
ReplaceBufferDownloads(overlap_id, new_buffer_id);
|
||||
DeleteBuffer(overlap_id);
|
||||
}
|
||||
|
||||
|
@ -1282,7 +1244,7 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
|
|||
boost::container::small_vector<BufferCopy, 1> copies;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
buffer.ForEachDownloadRange(cpu_addr, size, true, [&](u64 range_offset, u64 range_size) {
|
||||
buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) {
|
||||
copies.push_back(BufferCopy{
|
||||
.src_offset = range_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
|
@ -1353,6 +1315,18 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) {
|
|||
NotifyBufferDeletion();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id) {
|
||||
const auto replace = [old_buffer_id, new_buffer_id](std::vector<BufferId>& buffers) {
|
||||
std::ranges::replace(buffers, old_buffer_id, new_buffer_id);
|
||||
if (auto it = std::ranges::find(buffers, new_buffer_id); it != buffers.end()) {
|
||||
buffers.erase(std::remove(it + 1, buffers.end(), new_buffer_id), buffers.end());
|
||||
}
|
||||
};
|
||||
replace(uncommitted_downloads);
|
||||
std::ranges::for_each(committed_downloads, replace);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::NotifyBufferDeletion() {
|
||||
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
|
||||
|
|
|
@ -67,10 +67,10 @@ void Fermi2D::Blit() {
|
|||
};
|
||||
Surface src = regs.src;
|
||||
const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
|
||||
const auto is_copy_out_of_bound =
|
||||
const auto need_align_to_pitch =
|
||||
src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch && src.width == config.src_x1 &&
|
||||
config.src_x1 > static_cast<s32>(src.pitch / bytes_per_pixel) && config.src_x0 > 0;
|
||||
if (is_copy_out_of_bound) {
|
||||
if (need_align_to_pitch) {
|
||||
auto address = src.Address() + config.src_x0 * bytes_per_pixel;
|
||||
src.addr_upper = static_cast<u32>(address >> 32);
|
||||
src.addr_lower = static_cast<u32>(address);
|
||||
|
|
|
@ -96,23 +96,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void TryReleasePendingFences() {
|
||||
while (!fences.empty()) {
|
||||
TFence& current_fence = fences.front();
|
||||
if (ShouldWait() && !IsFenceSignaled(current_fence)) {
|
||||
return;
|
||||
}
|
||||
PopAsyncFlushes();
|
||||
if (current_fence->IsSemaphore()) {
|
||||
gpu_memory.template Write<u32>(current_fence->GetAddress(),
|
||||
current_fence->GetPayload());
|
||||
} else {
|
||||
gpu.IncrementSyncPoint(current_fence->GetPayload());
|
||||
}
|
||||
PopFence();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
|
||||
TTextureCache& texture_cache_, TTBufferCache& buffer_cache_,
|
||||
|
@ -142,6 +125,23 @@ protected:
|
|||
TQueryCache& query_cache;
|
||||
|
||||
private:
|
||||
void TryReleasePendingFences() {
|
||||
while (!fences.empty()) {
|
||||
TFence& current_fence = fences.front();
|
||||
if (ShouldWait() && !IsFenceSignaled(current_fence)) {
|
||||
return;
|
||||
}
|
||||
PopAsyncFlushes();
|
||||
if (current_fence->IsSemaphore()) {
|
||||
gpu_memory.template Write<u32>(current_fence->GetAddress(),
|
||||
current_fence->GetPayload());
|
||||
} else {
|
||||
gpu.IncrementSyncPoint(current_fence->GetPayload());
|
||||
}
|
||||
PopFence();
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldWait() const {
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() ||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
@ -84,17 +83,6 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
|
|||
rasterizer = renderer.ReadRasterizer();
|
||||
thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
|
||||
std::ref(dma_pusher), std::ref(state));
|
||||
gpu_sync_event = Core::Timing::CreateEvent(
|
||||
"GPUHostSyncCallback", [this](std::uintptr_t, std::chrono::nanoseconds) {
|
||||
if (!state.is_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
OnCommandListEnd();
|
||||
const auto time_interval = std::chrono::nanoseconds{500 * 1000};
|
||||
system.CoreTiming().ScheduleEvent(time_interval, gpu_sync_event);
|
||||
});
|
||||
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{500 * 1000}, gpu_sync_event);
|
||||
}
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
|
@ -140,9 +128,6 @@ void ThreadManager::ShutDown() {
|
|||
state.cv.notify_all();
|
||||
}
|
||||
|
||||
system.CoreTiming().UnscheduleEvent(gpu_sync_event, 0);
|
||||
system.CoreTiming().RemoveEvent(gpu_sync_event);
|
||||
|
||||
if (!thread.joinable()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,10 +20,6 @@ class DmaPusher;
|
|||
} // namespace Tegra
|
||||
|
||||
namespace Core {
|
||||
namespace Timing {
|
||||
class CoreTiming;
|
||||
struct EventType;
|
||||
} // namespace Timing
|
||||
namespace Frontend {
|
||||
class GraphicsContext;
|
||||
}
|
||||
|
@ -154,7 +150,6 @@ private:
|
|||
|
||||
SynchState state;
|
||||
std::thread thread;
|
||||
std::shared_ptr<Core::Timing::EventType> gpu_sync_event;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon::GPUThread
|
||||
|
|
|
@ -34,10 +34,6 @@ bool InnerFence::IsSignaled() const {
|
|||
if (is_stubbed) {
|
||||
return true;
|
||||
}
|
||||
if (scheduler.IsFree(wait_tick)) {
|
||||
return true;
|
||||
}
|
||||
scheduler.Refresh();
|
||||
return scheduler.IsFree(wait_tick);
|
||||
}
|
||||
|
||||
|
|
|
@ -604,7 +604,7 @@ void RasterizerVulkan::ReleaseFences() {
|
|||
if (!gpu.IsAsync()) {
|
||||
return;
|
||||
}
|
||||
fence_manager.TryReleasePendingFences();
|
||||
fence_manager.WaitPendingFences();
|
||||
}
|
||||
|
||||
void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||
|
|
|
@ -83,10 +83,6 @@ public:
|
|||
return master_semaphore->IsFree(tick);
|
||||
}
|
||||
|
||||
void Refresh() const noexcept {
|
||||
return master_semaphore->Refresh();
|
||||
}
|
||||
|
||||
/// Waits for the given tick to trigger on the GPU.
|
||||
void Wait(u64 tick) {
|
||||
master_semaphore->Wait(tick);
|
||||
|
|
|
@ -769,20 +769,20 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
|
|||
const ImageBase& src_image = slot_images[src_id];
|
||||
|
||||
// TODO: Deduplicate
|
||||
const std::optional dst_base = dst_image.TryFindBase(dst.Address());
|
||||
const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
|
||||
const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
|
||||
const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
|
||||
const std::optional src_base = src_image.TryFindBase(src.Address());
|
||||
const SubresourceRange src_range{.base = src_base.value(), .extent = {1, 1}};
|
||||
const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range);
|
||||
const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info);
|
||||
const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
|
||||
const Region2D src_region{
|
||||
Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y},
|
||||
Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y},
|
||||
};
|
||||
|
||||
const std::optional src_base = src_image.TryFindBase(src.Address());
|
||||
const SubresourceRange src_range{.base = src_base.value(), .extent = {1, 1}};
|
||||
const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range);
|
||||
const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info);
|
||||
const std::optional dst_base = dst_image.TryFindBase(dst.Address());
|
||||
const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
|
||||
const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
|
||||
const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
|
||||
const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
|
||||
const Region2D dst_region{
|
||||
Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y},
|
||||
|
|
|
@ -133,8 +133,8 @@ struct BufferImageCopy {
|
|||
};
|
||||
|
||||
struct BufferCopy {
|
||||
u64 src_offset;
|
||||
u64 dst_offset;
|
||||
size_t src_offset;
|
||||
size_t dst_offset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
|
|
|
@ -143,24 +143,25 @@ void MicroProfileWidget::hideEvent(QHideEvent* ev) {
|
|||
}
|
||||
|
||||
void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) {
|
||||
MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
|
||||
MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0);
|
||||
ev->accept();
|
||||
}
|
||||
|
||||
void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) {
|
||||
MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
|
||||
MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0);
|
||||
MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
|
||||
ev->accept();
|
||||
}
|
||||
|
||||
void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
|
||||
MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
|
||||
MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale, 0);
|
||||
MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
|
||||
ev->accept();
|
||||
}
|
||||
|
||||
void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
|
||||
MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, ev->delta() / 120);
|
||||
MicroProfileMousePosition(ev->pos().x() / x_scale, ev->pos().y() / y_scale,
|
||||
ev->angleDelta().y() / 120);
|
||||
ev->accept();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue