summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/make_unique_for_overwrite.h25
-rw-r--r--src/common/scratch_buffer.h95
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/scratch_buffer.cpp199
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h11
-rw-r--r--src/video_core/dma_pusher.cpp19
-rw-r--r--src/video_core/dma_pusher.h8
-rw-r--r--src/video_core/engines/engine_upload.cpp4
-rw-r--r--src/video_core/engines/engine_upload.h7
-rw-r--r--src/video_core/engines/maxwell_dma.cpp34
-rw-r--r--src/video_core/engines/maxwell_dma.h8
-rw-r--r--src/video_core/host1x/vic.cpp6
-rw-r--r--src/video_core/host1x/vic.h7
-rw-r--r--src/yuzu/bootmanager.cpp1
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/main.cpp72
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu/util/overlay_dialog.cpp9
-rw-r--r--src/yuzu/util/overlay_dialog.h1
-rw-r--r--src/yuzu_cmd/CMakeLists.txt9
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
22 files changed, 461 insertions, 73 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 25b22a281..eb05e46a8 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -78,6 +78,7 @@ add_library(common STATIC
logging/types.h
lz4_compression.cpp
lz4_compression.h
+ make_unique_for_overwrite.h
math_util.h
memory_detect.cpp
memory_detect.h
@@ -101,6 +102,7 @@ add_library(common STATIC
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
scm_rev.h
scope_exit.h
+ scratch_buffer.h
settings.cpp
settings.h
settings_input.cpp
diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h
new file mode 100644
index 000000000..c7413cf51
--- /dev/null
+++ b/src/common/make_unique_for_overwrite.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <type_traits>
+
+namespace Common {
+
+template <class T>
+requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() {
+ return std::unique_ptr<T>(new T);
+}
+
+template <class T>
+requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
+ return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
+}
+
+template <class T, class... Args>
+requires std::is_bounded_array_v<T>
+void make_unique_for_overwrite(Args&&...) = delete;
+
+} // namespace Common
diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h
new file mode 100644
index 000000000..1245a5086
--- /dev/null
+++ b/src/common/scratch_buffer.h
@@ -0,0 +1,95 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/make_unique_for_overwrite.h"
+
+namespace Common {
+
+/**
+ * ScratchBuffer class
+ * This class creates a default initialized heap allocated buffer for cases such as intermediate
+ * buffers being copied into entirely, where value initializing members during allocation or resize
+ * is redundant.
+ */
+template <typename T>
+class ScratchBuffer {
+public:
+ ScratchBuffer() = default;
+
+ explicit ScratchBuffer(size_t initial_capacity)
+ : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
+ buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
+
+ ~ScratchBuffer() = default;
+
+ /// This will only grow the buffer's capacity if size is greater than the current capacity.
+ /// The previously held data will remain intact.
+ void resize(size_t size) {
+ if (size > buffer_capacity) {
+ auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
+ std::move(buffer.get(), buffer.get() + buffer_capacity, new_buffer.get());
+ buffer = std::move(new_buffer);
+ buffer_capacity = size;
+ }
+ last_requested_size = size;
+ }
+
+ /// This will only grow the buffer's capacity if size is greater than the current capacity.
+ /// The previously held data will be destroyed if a reallocation occurs.
+ void resize_destructive(size_t size) {
+ if (size > buffer_capacity) {
+ buffer_capacity = size;
+ buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
+ }
+ last_requested_size = size;
+ }
+
+ [[nodiscard]] T* data() noexcept {
+ return buffer.get();
+ }
+
+ [[nodiscard]] const T* data() const noexcept {
+ return buffer.get();
+ }
+
+ [[nodiscard]] T* begin() noexcept {
+ return data();
+ }
+
+ [[nodiscard]] const T* begin() const noexcept {
+ return data();
+ }
+
+ [[nodiscard]] T* end() noexcept {
+ return data() + last_requested_size;
+ }
+
+ [[nodiscard]] const T* end() const noexcept {
+ return data() + last_requested_size;
+ }
+
+ [[nodiscard]] T& operator[](size_t i) {
+ return buffer[i];
+ }
+
+ [[nodiscard]] const T& operator[](size_t i) const {
+ return buffer[i];
+ }
+
+ [[nodiscard]] size_t size() const noexcept {
+ return last_requested_size;
+ }
+
+ [[nodiscard]] size_t capacity() const noexcept {
+ return buffer_capacity;
+ }
+
+private:
+ size_t last_requested_size{};
+ size_t buffer_capacity{};
+ std::unique_ptr<T[]> buffer{};
+};
+
+} // namespace Common
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 348d1edf4..6a4022e45 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -8,6 +8,7 @@ add_executable(tests
common/host_memory.cpp
common/param_package.cpp
common/ring_buffer.cpp
+ common/scratch_buffer.cpp
common/unique_function.cpp
core/core_timing.cpp
core/internal_network/network.cpp
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp
new file mode 100644
index 000000000..b602c8d0a
--- /dev/null
+++ b/src/tests/common/scratch_buffer.cpp
@@ -0,0 +1,199 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <algorithm>
+#include <array>
+#include <span>
+#include <catch2/catch.hpp>
+#include "common/common_types.h"
+#include "common/scratch_buffer.h"
+
+namespace Common {
+
+TEST_CASE("ScratchBuffer: Basic Test", "[common]") {
+ ScratchBuffer<u8> buf;
+
+ REQUIRE(buf.size() == 0U);
+ REQUIRE(buf.capacity() == 0U);
+
+ std::array<u8, 10> payload;
+ payload.fill(66);
+
+ buf.resize(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ std::memcpy(buf.data(), payload.data(), payload.size());
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+}
+
+TEST_CASE("ScratchBuffer: resize_destructive Grow", "[common]") {
+ std::array<u8, 10> payload;
+ payload.fill(66);
+
+ ScratchBuffer<u8> buf(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ // Increasing the size should reallocate the buffer
+ buf.resize_destructive(payload.size() * 2);
+ REQUIRE(buf.size() == payload.size() * 2);
+ REQUIRE(buf.capacity() == payload.size() * 2);
+
+ // Since the buffer is not value initialized, reading its data will be garbage
+}
+
+TEST_CASE("ScratchBuffer: resize_destructive Shrink", "[common]") {
+ std::array<u8, 10> payload;
+ payload.fill(66);
+
+ ScratchBuffer<u8> buf(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ std::memcpy(buf.data(), payload.data(), payload.size());
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+
+ // Decreasing the size should not cause a buffer reallocation
+ // This can be tested by ensuring the buffer capacity and data has not changed,
+ buf.resize_destructive(1U);
+ REQUIRE(buf.size() == 1U);
+ REQUIRE(buf.capacity() == payload.size());
+
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+}
+
+TEST_CASE("ScratchBuffer: resize Grow u8", "[common]") {
+ std::array<u8, 10> payload;
+ payload.fill(66);
+
+ ScratchBuffer<u8> buf(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ std::memcpy(buf.data(), payload.data(), payload.size());
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+
+ // Increasing the size should reallocate the buffer
+ buf.resize(payload.size() * 2);
+ REQUIRE(buf.size() == payload.size() * 2);
+ REQUIRE(buf.capacity() == payload.size() * 2);
+
+ // resize() keeps the previous data intact
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+}
+
+TEST_CASE("ScratchBuffer: resize Grow u64", "[common]") {
+ std::array<u64, 10> payload;
+ payload.fill(6666);
+
+ ScratchBuffer<u64> buf(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ std::memcpy(buf.data(), payload.data(), payload.size() * sizeof(u64));
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+
+ // Increasing the size should reallocate the buffer
+ buf.resize(payload.size() * 2);
+ REQUIRE(buf.size() == payload.size() * 2);
+ REQUIRE(buf.capacity() == payload.size() * 2);
+
+ // resize() keeps the previous data intact
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+}
+
+TEST_CASE("ScratchBuffer: resize Shrink", "[common]") {
+ std::array<u8, 10> payload;
+ payload.fill(66);
+
+ ScratchBuffer<u8> buf(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ std::memcpy(buf.data(), payload.data(), payload.size());
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+
+ // Decreasing the size should not cause a buffer reallocation
+ // This can be tested by ensuring the buffer capacity and data has not changed,
+ buf.resize(1U);
+ REQUIRE(buf.size() == 1U);
+ REQUIRE(buf.capacity() == payload.size());
+
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+}
+
+TEST_CASE("ScratchBuffer: Span Size", "[common]") {
+ std::array<u8, 10> payload;
+ payload.fill(66);
+
+ ScratchBuffer<u8> buf(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ std::memcpy(buf.data(), payload.data(), payload.size());
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+
+ buf.resize(3U);
+ REQUIRE(buf.size() == 3U);
+ REQUIRE(buf.capacity() == payload.size());
+
+ const auto buf_span = std::span<u8>(buf);
+ // The span size is the last requested size of the buffer, not its capacity
+ REQUIRE(buf_span.size() == buf.size());
+
+ for (size_t i = 0; i < buf_span.size(); ++i) {
+ REQUIRE(buf_span[i] == buf[i]);
+ REQUIRE(buf_span[i] == payload[i]);
+ }
+}
+
+TEST_CASE("ScratchBuffer: Span Writes", "[common]") {
+ std::array<u8, 10> payload;
+ payload.fill(66);
+
+ ScratchBuffer<u8> buf(payload.size());
+ REQUIRE(buf.size() == payload.size());
+ REQUIRE(buf.capacity() == payload.size());
+
+ std::memcpy(buf.data(), payload.data(), payload.size());
+ for (size_t i = 0; i < payload.size(); ++i) {
+ REQUIRE(buf[i] == payload[i]);
+ }
+
+ buf.resize(3U);
+ REQUIRE(buf.size() == 3U);
+ REQUIRE(buf.capacity() == payload.size());
+
+ const auto buf_span = std::span<u8>(buf);
+ REQUIRE(buf_span.size() == buf.size());
+
+ for (size_t i = 0; i < buf_span.size(); ++i) {
+ const auto new_value = static_cast<u8>(i + 1U);
+ // Writes to a span of the scratch buffer will propogate to the buffer itself
+ buf_span[i] = new_value;
+ REQUIRE(buf[i] == new_value);
+ }
+}
+
+} // namespace Common
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 502b4d90a..6c8d98946 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -20,6 +20,7 @@
#include "common/lru_cache.h"
#include "common/microprofile.h"
#include "common/polyfill_ranges.h"
+#include "common/scratch_buffer.h"
#include "common/settings.h"
#include "core/memory.h"
#include "video_core/buffer_cache/buffer_base.h"
@@ -422,8 +423,7 @@ private:
IntervalSet common_ranges;
std::deque<IntervalSet> committed_ranges;
- size_t immediate_buffer_capacity = 0;
- std::unique_ptr<u8[]> immediate_buffer_alloc;
+ Common::ScratchBuffer<u8> immediate_buffer_alloc;
struct LRUItemParams {
using ObjectType = BufferId;
@@ -1926,11 +1926,8 @@ std::span<const u8> BufferCache<P>::ImmediateBufferWithData(VAddr cpu_addr, size
template <class P>
std::span<u8> BufferCache<P>::ImmediateBuffer(size_t wanted_capacity) {
- if (wanted_capacity > immediate_buffer_capacity) {
- immediate_buffer_capacity = wanted_capacity;
- immediate_buffer_alloc = std::make_unique<u8[]>(wanted_capacity);
- }
- return std::span<u8>(immediate_buffer_alloc.get(), wanted_capacity);
+ immediate_buffer_alloc.resize_destructive(wanted_capacity);
+ return std::span<u8>(immediate_buffer_alloc.data(), wanted_capacity);
}
template <class P>
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 9835e3ac1..322de2606 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -56,7 +56,7 @@ bool DmaPusher::Step() {
if (command_list.prefetch_command_list.size()) {
// Prefetched command list from nvdrv, used for things like synchronization
- command_headers = std::move(command_list.prefetch_command_list);
+ ProcessCommands(command_list.prefetch_command_list);
dma_pushbuffer.pop();
} else {
const CommandListHeader command_list_header{
@@ -74,7 +74,7 @@ bool DmaPusher::Step() {
}
// Push buffer non-empty, read a word
- command_headers.resize(command_list_header.size);
+ command_headers.resize_destructive(command_list_header.size);
if (Settings::IsGPULevelHigh()) {
memory_manager.ReadBlock(dma_get, command_headers.data(),
command_list_header.size * sizeof(u32));
@@ -82,16 +82,21 @@ bool DmaPusher::Step() {
memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(),
command_list_header.size * sizeof(u32));
}
+ ProcessCommands(command_headers);
}
- for (std::size_t index = 0; index < command_headers.size();) {
- const CommandHeader& command_header = command_headers[index];
+
+ return true;
+}
+
+void DmaPusher::ProcessCommands(std::span<const CommandHeader> commands) {
+ for (std::size_t index = 0; index < commands.size();) {
+ const CommandHeader& command_header = commands[index];
if (dma_state.method_count) {
// Data word of methods command
if (dma_state.non_incrementing) {
const u32 max_write = static_cast<u32>(
- std::min<std::size_t>(index + dma_state.method_count, command_headers.size()) -
- index);
+ std::min<std::size_t>(index + dma_state.method_count, commands.size()) - index);
CallMultiMethod(&command_header.argument, max_write);
dma_state.method_count -= max_write;
dma_state.is_last_call = true;
@@ -142,8 +147,6 @@ bool DmaPusher::Step() {
}
index++;
}
-
- return true;
}
void DmaPusher::SetState(const CommandHeader& command_header) {
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 938f0f11c..6f00de937 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -4,11 +4,13 @@
#pragma once
#include <array>
+#include <span>
#include <vector>
#include <queue>
#include "common/bit_field.h"
#include "common/common_types.h"
+#include "common/scratch_buffer.h"
#include "video_core/engines/engine_interface.h"
#include "video_core/engines/puller.h"
@@ -136,13 +138,15 @@ private:
static constexpr u32 non_puller_methods = 0x40;
static constexpr u32 max_subchannels = 8;
bool Step();
+ void ProcessCommands(std::span<const CommandHeader> commands);
void SetState(const CommandHeader& command_header);
void CallMethod(u32 argument) const;
void CallMultiMethod(const u32* base_start, u32 num_methods) const;
- std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once
+ Common::ScratchBuffer<CommandHeader>
+ command_headers; ///< Buffer for list of commands fetched at once
std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
std::size_t dma_pushbuffer_subindex{}; ///< Index within a command list within the pushbuffer
@@ -159,7 +163,7 @@ private:
DmaState dma_state{};
bool dma_increment_once{};
- bool ib_enable{true}; ///< IB mode enabled
+ const bool ib_enable{true}; ///< IB mode enabled
std::array<Engines::EngineInterface*, max_subchannels> subchannels{};
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp
index e4f8331ab..cea1dd8b0 100644
--- a/src/video_core/engines/engine_upload.cpp
+++ b/src/video_core/engines/engine_upload.cpp
@@ -24,7 +24,7 @@ void State::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) {
void State::ProcessExec(const bool is_linear_) {
write_offset = 0;
copy_size = regs.line_length_in * regs.line_count;
- inner_buffer.resize(copy_size);
+ inner_buffer.resize_destructive(copy_size);
is_linear = is_linear_;
}
@@ -70,7 +70,7 @@ void State::ProcessData(std::span<const u8> read_buffer) {
const std::size_t dst_size = Tegra::Texture::CalculateSize(
true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth,
regs.dest.BlockHeight(), regs.dest.BlockDepth());
- tmp_buffer.resize(dst_size);
+ tmp_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size);
Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width,
regs.dest.height, regs.dest.depth, x_offset, regs.dest.y,
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h
index 94fafd9dc..7242d2529 100644
--- a/src/video_core/engines/engine_upload.h
+++ b/src/video_core/engines/engine_upload.h
@@ -4,9 +4,10 @@
#pragma once
#include <span>
-#include <vector>
+
#include "common/bit_field.h"
#include "common/common_types.h"
+#include "common/scratch_buffer.h"
namespace Tegra {
class MemoryManager;
@@ -73,8 +74,8 @@ private:
u32 write_offset = 0;
u32 copy_size = 0;
- std::vector<u8> inner_buffer;
- std::vector<u8> tmp_buffer;
+ Common::ScratchBuffer<u8> inner_buffer;
+ Common::ScratchBuffer<u8> tmp_buffer;
bool is_linear = false;
Registers& regs;
MemoryManager& memory_manager;
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a189e60ae..f73d7bf0f 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -184,12 +184,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
const size_t src_size =
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
- if (read_buffer.size() < src_size) {
- read_buffer.resize(src_size);
- }
- if (write_buffer.size() < dst_size) {
- write_buffer.resize(dst_size);
- }
+ read_buffer.resize_destructive(src_size);
+ write_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
@@ -235,12 +231,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
- if (read_buffer.size() < src_size) {
- read_buffer.resize(src_size);
- }
- if (write_buffer.size() < dst_size) {
- write_buffer.resize(dst_size);
- }
+ read_buffer.resize_destructive(src_size);
+ write_buffer.resize_destructive(dst_size);
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
if (Settings::IsGPULevelExtreme()) {
@@ -269,12 +261,8 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
pos_x = pos_x % x_in_gob;
pos_y = pos_y % 8;
- if (read_buffer.size() < src_size) {
- read_buffer.resize(src_size);
- }
- if (write_buffer.size() < dst_size) {
- write_buffer.resize(dst_size);
- }
+ read_buffer.resize_destructive(src_size);
+ write_buffer.resize_destructive(dst_size);
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size);
@@ -333,14 +321,10 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() {
const u32 pitch = x_elements * bytes_per_pixel;
const size_t mid_buffer_size = pitch * regs.line_count;
- if (read_buffer.size() < src_size) {
- read_buffer.resize(src_size);
- }
- if (write_buffer.size() < dst_size) {
- write_buffer.resize(dst_size);
- }
+ read_buffer.resize_destructive(src_size);
+ write_buffer.resize_destructive(dst_size);
- intermediate_buffer.resize(mid_buffer_size);
+ intermediate_buffer.resize_destructive(mid_buffer_size);
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index d40d3d302..c88191a61 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -6,8 +6,10 @@
#include <array>
#include <cstddef>
#include <vector>
+
#include "common/bit_field.h"
#include "common/common_types.h"
+#include "common/scratch_buffer.h"
#include "video_core/engines/engine_interface.h"
namespace Core {
@@ -234,9 +236,9 @@ private:
MemoryManager& memory_manager;
VideoCore::RasterizerInterface* rasterizer = nullptr;
- std::vector<u8> read_buffer;
- std::vector<u8> write_buffer;
- std::vector<u8> intermediate_buffer;
+ Common::ScratchBuffer<u8> read_buffer;
+ Common::ScratchBuffer<u8> write_buffer;
+ Common::ScratchBuffer<u8> intermediate_buffer;
static constexpr std::size_t NUM_REGS = 0x800;
struct Regs {
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp
index ac0b7d20e..36a04e4e0 100644
--- a/src/video_core/host1x/vic.cpp
+++ b/src/video_core/host1x/vic.cpp
@@ -155,7 +155,7 @@ void Vic::WriteRGBFrame(const AVFrame* frame, const VicConfig& config) {
// swizzle pitch linear to block linear
const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
const auto size = Texture::CalculateSize(true, 4, width, height, 1, block_height, 0);
- luma_buffer.resize(size);
+ luma_buffer.resize_destructive(size);
std::span<const u8> frame_buff(converted_frame_buf_addr, 4 * width * height);
Texture::SwizzleSubrect(luma_buffer, frame_buff, 4, width, height, 1, 0, 0, width, height,
block_height, 0, width * 4);
@@ -181,8 +181,8 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
const auto stride = static_cast<size_t>(frame->linesize[0]);
- luma_buffer.resize(aligned_width * surface_height);
- chroma_buffer.resize(aligned_width * surface_height / 2);
+ luma_buffer.resize_destructive(aligned_width * surface_height);
+ chroma_buffer.resize_destructive(aligned_width * surface_height / 2);
// Populate luma buffer
const u8* luma_src = frame->data[0];
diff --git a/src/video_core/host1x/vic.h b/src/video_core/host1x/vic.h
index 2b78786e8..3d9753047 100644
--- a/src/video_core/host1x/vic.h
+++ b/src/video_core/host1x/vic.h
@@ -4,8 +4,9 @@
#pragma once
#include <memory>
-#include <vector>
+
#include "common/common_types.h"
+#include "common/scratch_buffer.h"
struct SwsContext;
@@ -49,8 +50,8 @@ private:
/// size does not change during a stream
using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>;
AVMallocPtr converted_frame_buffer;
- std::vector<u8> luma_buffer;
- std::vector<u8> chroma_buffer;
+ Common::ScratchBuffer<u8> luma_buffer;
+ Common::ScratchBuffer<u8> chroma_buffer;
GPUVAddr config_struct_address{};
GPUVAddr output_surface_luma_address{};
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 13782869d..3d560f303 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -78,7 +78,6 @@ void EmuThread::run() {
gpu.Start();
m_system.GetCpuManager().OnGpuReady();
- m_system.RegisterExitCallback([this] { m_stop_source.request_stop(); });
if (m_system.DebuggerEnabled()) {
m_system.InitializeDebugger();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 1c2e76369..eca16b313 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -80,7 +80,7 @@ public:
* @return True if the emulation thread is running, otherwise false
*/
bool IsRunning() const {
- return m_is_running.load();
+ return m_is_running.load() || m_should_run;
}
/**
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 820f60e61..6121711e0 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1550,8 +1550,9 @@ void GMainWindow::AllowOSSleep() {
bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
// Shutdown previous session if the emu thread is still active...
- if (emu_thread != nullptr)
+ if (emu_thread != nullptr) {
ShutdownGame();
+ }
if (!render_window->InitRenderTarget()) {
return false;
@@ -1710,6 +1711,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
system->RegisterExecuteProgramCallback(
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
+ system->RegisterExitCallback([this] {
+ emu_thread->ForceStop();
+ render_window->Exit();
+ });
+
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
@@ -1779,7 +1785,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
OnStartGame();
}
-void GMainWindow::ShutdownGame() {
+void GMainWindow::OnShutdownBegin() {
if (!emulation_running) {
return;
}
@@ -1802,13 +1808,40 @@ void GMainWindow::ShutdownGame() {
emit EmulationStopping();
- // Wait for emulation thread to complete and delete it
- if (system->DebuggerEnabled() || !emu_thread->wait(5000)) {
+ shutdown_timer.setSingleShot(true);
+ shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000);
+ connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
+ connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped);
+
+ // Disable everything to prevent anything from being triggered here
+ ui->action_Pause->setEnabled(false);
+ ui->action_Restart->setEnabled(false);
+ ui->action_Stop->setEnabled(false);
+}
+
+void GMainWindow::OnShutdownBeginDialog() {
+ shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."),
+ QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter);
+ shutdown_dialog->open();
+}
+
+void GMainWindow::OnEmulationStopTimeExpired() {
+ if (emu_thread) {
emu_thread->ForceStop();
- emu_thread->wait();
}
+}
+
+void GMainWindow::OnEmulationStopped() {
+ shutdown_timer.stop();
+ emu_thread->disconnect();
+ emu_thread->wait();
emu_thread = nullptr;
+ if (shutdown_dialog) {
+ shutdown_dialog->deleteLater();
+ shutdown_dialog = nullptr;
+ }
+
emulation_running = false;
discord_rpc->Update();
@@ -1854,6 +1887,20 @@ void GMainWindow::ShutdownGame() {
// When closing the game, destroy the GLWindow to clear the context after the game is closed
render_window->ReleaseRenderTarget();
+
+ Settings::RestoreGlobalState(system->IsPoweredOn());
+ system->HIDCore().ReloadInputDevices();
+ UpdateStatusButtons();
+}
+
+void GMainWindow::ShutdownGame() {
+ if (!emulation_running) {
+ return;
+ }
+
+ OnShutdownBegin();
+ OnEmulationStopTimeExpired();
+ OnEmulationStopped();
}
void GMainWindow::StoreRecentFile(const QString& filename) {
@@ -2956,11 +3003,8 @@ void GMainWindow::OnStopGame() {
return;
}
- ShutdownGame();
-
- Settings::RestoreGlobalState(system->IsPoweredOn());
- system->HIDCore().ReloadInputDevices();
- UpdateStatusButtons();
+ OnShutdownBegin();
+ OnShutdownBeginDialog();
}
void GMainWindow::OnLoadComplete() {
@@ -4047,10 +4091,6 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
// Shutdown session if the emu thread is active...
if (emu_thread != nullptr) {
ShutdownGame();
-
- Settings::RestoreGlobalState(system->IsPoweredOn());
- system->HIDCore().ReloadInputDevices();
- UpdateStatusButtons();
}
render_window->close();
@@ -4143,6 +4183,10 @@ bool GMainWindow::ConfirmForceLockedExit() {
}
void GMainWindow::RequestGameExit() {
+ if (!system->IsPoweredOn()) {
+ return;
+ }
+
auto& sm{system->ServiceManager()};
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 5b84c7a00..95220b063 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -29,6 +29,7 @@ class GImageInfo;
class GRenderWindow;
class LoadingScreen;
class MicroProfileDialog;
+class OverlayDialog;
class ProfilerWidget;
class ControllerDialog;
class QLabel;
@@ -335,6 +336,10 @@ private slots:
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnLanguageChanged(const QString& locale);
void OnMouseActivity();
+ void OnShutdownBegin();
+ void OnShutdownBeginDialog();
+ void OnEmulationStopped();
+ void OnEmulationStopTimeExpired();
private:
QString GetGameListErrorRemoving(InstalledEntryType type) const;
@@ -384,6 +389,8 @@ private:
GRenderWindow* render_window;
GameList* game_list;
LoadingScreen* loading_screen;
+ QTimer shutdown_timer;
+ OverlayDialog* shutdown_dialog{};
GameListPlaceholder* game_list_placeholder;
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index 3fa3d0afb..796f5bf41 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -3,6 +3,7 @@
#include <QKeyEvent>
#include <QScreen>
+#include <QWindow>
#include "core/core.h"
#include "core/hid/hid_types.h"
@@ -162,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() {
const auto height = static_cast<float>(parentWidget()->height());
// High DPI
- const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
+ const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f;
const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
const auto body_text_font_size =
@@ -259,3 +260,9 @@ void OverlayDialog::InputThread() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
+
+void OverlayDialog::keyPressEvent(QKeyEvent* e) {
+ if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) {
+ QDialog::keyPressEvent(e);
+ }
+}
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
index 39c44393c..872283d61 100644
--- a/src/yuzu/util/overlay_dialog.h
+++ b/src/yuzu/util/overlay_dialog.h
@@ -94,6 +94,7 @@ private:
/// The thread where input is being polled and processed.
void InputThread();
+ void keyPressEvent(QKeyEvent* e) override;
std::unique_ptr<Ui::OverlayDialog> ui;
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index f6eeb9d8d..61b6cc4e0 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -49,6 +49,15 @@ if(UNIX AND NOT APPLE)
install(TARGETS yuzu-cmd)
endif()
+if(WIN32)
+ # compile as a win32 gui application instead of a console application
+ if(MSVC)
+ set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
+ elseif(MINGW)
+ set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
+ endif()
+endif()
+
if (MSVC)
include(CopyYuzuSDLDeps)
copy_yuzu_SDL_deps(yuzu-cmd)
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index a80649703..91133569d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -174,6 +174,13 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
/// Application entry point
int main(int argc, char** argv) {
+#ifdef _WIN32
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ freopen("CONOUT$", "wb", stdout);
+ freopen("CONOUT$", "wb", stderr);
+ }
+#endif
+
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();