summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/logging/backend.cpp11
-rw-r--r--src/common/logging/backend.h14
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp3
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h4
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp53
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp146
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h132
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/video_core/surface.cpp29
-rw-r--r--src/video_core/surface.h108
-rw-r--r--src/video_core/textures/astc.cpp32
-rw-r--r--src/video_core/textures/astc.h2
-rw-r--r--src/video_core/textures/decoders.cpp12
-rw-r--r--src/video_core/textures/decoders.h4
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp6
-rw-r--r--src/yuzu/main.cpp11
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
23 files changed, 392 insertions, 195 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 6d5218465..5753b871a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -12,7 +12,8 @@
#include <thread>
#include <vector>
#ifdef _WIN32
-#include <share.h> // For _SH_DENYWR
+#include <share.h> // For _SH_DENYWR
+#include <windows.h> // For OutputDebugStringA
#else
#define _SH_DENYWR 0
#endif
@@ -139,12 +140,18 @@ void FileBackend::Write(const Entry& entry) {
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
return;
}
- bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
+ bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
file.Flush();
}
}
+void DebuggerBackend::Write(const Entry& entry) {
+#ifdef _WIN32
+ ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
+#endif
+}
+
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \
CLS(Log) \
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 11edbf1b6..91bb0c309 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -103,6 +103,20 @@ private:
std::size_t bytes_written;
};
+/**
+ * Backend that writes to Visual Studio's output window
+ */
+class DebuggerBackend : public Backend {
+public:
+ static const char* Name() {
+ return "debugger";
+ }
+ const char* GetName() const override {
+ return Name();
+ }
+ void Write(const Entry& entry) override;
+};
+
void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(std::string_view backend_name);
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 7168c6a10..783c39503 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -161,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
std::size_t worker_sz = WorkerBufferSize(channel_count);
- ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
+ ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
static_cast<OpusDecoder*>(operator new(worker_sz))};
if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 4b4d1324f..1ef789bd0 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -427,6 +427,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
}
Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
+ // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
+ // be signalled at least once, and signaled after a new controller is connected?
+ styleset_changed_event->Signal();
return styleset_changed_event;
}
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index a9aa9ec78..a45fd4954 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -96,6 +96,8 @@ public:
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
+
+ ReloadInputDevices();
}
void ActivateController(HidController controller) {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 2fe81a560..8cff5eb71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -58,9 +58,9 @@ public:
/// Rotate source image 90 degrees clockwise
Rotate90 = 0x04,
/// Rotate source image 180 degrees
- Roate180 = 0x03,
+ Rotate180 = 0x03,
/// Rotate source image 270 degrees clockwise
- Roate270 = 0x07,
+ Rotate270 = 0x07,
};
struct Buffer {
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index ddb1a1d69..0b1cc1290 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -33,6 +33,7 @@ add_library(video_core STATIC
renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.cpp
renderer_opengl/gl_rasterizer_cache.h
+ renderer_opengl/gl_resource_manager.cpp
renderer_opengl/gl_resource_manager.h
renderer_opengl/gl_shader_cache.cpp
renderer_opengl/gl_shader_cache.h
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 57f501177..091d9f043 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -16,6 +16,7 @@
#include "core/settings.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
+#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/utils.h"
#include "video_core/surface.h"
#include "video_core/textures/astc.h"
@@ -58,16 +59,14 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only,
bool uncompressed) const {
- const u32 compression_factor{GetCompressionFactor(pixel_format)};
+ const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
+ const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
u32 m_depth = (layer_only ? 1U : depth);
u32 m_width = MipWidth(mip_level);
u32 m_height = MipHeight(mip_level);
- m_width = uncompressed ? m_width
- : std::max(1U, (m_width + compression_factor - 1) / compression_factor);
- m_height = uncompressed
- ? m_height
- : std::max(1U, (m_height + compression_factor - 1) / compression_factor);
+ m_width = uncompressed ? m_width : std::max(1U, (m_width + tile_x - 1) / tile_x);
+ m_height = uncompressed ? m_height : std::max(1U, (m_height + tile_y - 1) / tile_y);
m_depth = std::max(1U, m_depth >> mip_level);
u32 m_block_height = MipBlockHeight(mip_level);
u32 m_block_depth = MipBlockDepth(mip_level);
@@ -128,6 +127,13 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
params.target = SurfaceTarget::Texture2D;
}
break;
+ case SurfaceTarget::TextureCubeArray:
+ params.depth = config.tic.Depth() * 6;
+ if (!entry.IsArray()) {
+ ASSERT(params.depth == 6);
+ params.target = SurfaceTarget::TextureCubemap;
+ }
+ break;
default:
LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target));
UNREACHABLE();
@@ -305,6 +311,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
+ {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
+ {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
// Depth formats
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -334,6 +342,8 @@ static GLenum SurfaceTargetToGL(SurfaceTarget target) {
return GL_TEXTURE_2D_ARRAY;
case SurfaceTarget::TextureCubemap:
return GL_TEXTURE_CUBE_MAP;
+ case SurfaceTarget::TextureCubeArray:
+ return GL_TEXTURE_CUBE_MAP_ARRAY_ARB;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
UNREACHABLE();
@@ -364,15 +374,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
// pixel values.
- const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
+ const u32 tile_size_x{GetDefaultBlockWidth(format)};
+ const u32 tile_size_y{GetDefaultBlockHeight(format)};
if (morton_to_gl) {
- const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
- addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
+ const std::vector<u8> data =
+ Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
+ stride, height, depth, block_height, block_depth);
const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
memcpy(gl_buffer, data.data(), size_to_copy);
} else {
- Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth,
+ Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
+ (height + tile_size_y - 1) / tile_size_y, depth,
bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
gl_buffer, false, block_height, block_depth);
}
@@ -440,6 +453,8 @@ static constexpr GLConversionArray morton_to_gl_fns = {
MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
+ MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
+ MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::Z16>,
MortonCopy<true, PixelFormat::Z24S8>,
@@ -508,6 +523,8 @@ static constexpr GLConversionArray gl_to_morton_fns = {
nullptr,
nullptr,
nullptr,
+ nullptr,
+ nullptr,
MortonCopy<false, PixelFormat::Z32F>,
MortonCopy<false, PixelFormat::Z16>,
MortonCopy<false, PixelFormat::Z24S8>,
@@ -754,6 +771,7 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
break;
case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubeArray:
glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
static_cast<GLsizei>(dst_params.depth), dest_format.format,
dest_format.type, nullptr);
@@ -806,6 +824,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
break;
case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubeArray:
glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
params.depth);
@@ -897,21 +916,24 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
* typical desktop GPUs.
*/
static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
- u32 width, u32 height) {
+ u32 width, u32 height, u32 depth) {
switch (pixel_format) {
case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_8X8:
case PixelFormat::ASTC_2D_8X5:
case PixelFormat::ASTC_2D_5X4:
+ case PixelFormat::ASTC_2D_5X5:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
- case PixelFormat::ASTC_2D_5X4_SRGB: {
+ case PixelFormat::ASTC_2D_5X4_SRGB:
+ case PixelFormat::ASTC_2D_5X5_SRGB: {
// Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
u32 block_width{};
u32 block_height{};
std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
- data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height);
+ data =
+ Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
break;
}
case PixelFormat::S8Z24:
@@ -971,7 +993,7 @@ void CachedSurface::LoadGLBuffer() {
}
for (u32 i = 0; i < params.max_mip_level; i++)
ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
- params.MipHeight(i));
+ params.MipHeight(i), params.MipDepth(i));
}
MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
@@ -1055,6 +1077,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
&gl_buffer[mip_map][buffer_offset]);
break;
case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubeArray:
glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.MipHeight(mip_map)),
@@ -1104,6 +1127,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
break;
case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubeArray:
glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
@@ -1307,6 +1331,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
case SurfaceTarget::TextureCubemap:
case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubeArray:
AccurateCopySurface(old_surface, new_surface);
break;
default:
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index f255f4419..c0b6bc4e6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -49,6 +49,8 @@ struct SurfaceParams {
return "Texture2DArray";
case SurfaceTarget::TextureCubemap:
return "TextureCubemap";
+ case SurfaceTarget::TextureCubeArray:
+ return "TextureCubeArray";
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
UNREACHABLE();
@@ -139,7 +141,7 @@ struct SurfaceParams {
}
u32 MipDepth(u32 mip_level) const {
- return std::max(1U, depth >> mip_level);
+ return is_layered ? depth : std::max(1U, depth >> mip_level);
}
// Auto block resizing algorithm from:
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
new file mode 100644
index 000000000..c10863337
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -0,0 +1,146 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <utility>
+#include <glad/glad.h>
+#include "common/common_types.h"
+#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_shader_util.h"
+#include "video_core/renderer_opengl/gl_state.h"
+
+namespace OpenGL {
+
+void OGLTexture::Create() {
+ if (handle != 0)
+ return;
+ glGenTextures(1, &handle);
+}
+
+void OGLTexture::Release() {
+ if (handle == 0)
+ return;
+ glDeleteTextures(1, &handle);
+ OpenGLState::GetCurState().UnbindTexture(handle).Apply();
+ handle = 0;
+}
+
+void OGLSampler::Create() {
+ if (handle != 0)
+ return;
+ glGenSamplers(1, &handle);
+}
+
+void OGLSampler::Release() {
+ if (handle == 0)
+ return;
+ glDeleteSamplers(1, &handle);
+ OpenGLState::GetCurState().ResetSampler(handle).Apply();
+ handle = 0;
+}
+
+void OGLShader::Create(const char* source, GLenum type) {
+ if (handle != 0)
+ return;
+ if (source == nullptr)
+ return;
+ handle = GLShader::LoadShader(source, type);
+}
+
+void OGLShader::Release() {
+ if (handle == 0)
+ return;
+ glDeleteShader(handle);
+ handle = 0;
+}
+
+void OGLProgram::CreateFromSource(const char* vert_shader, const char* geo_shader,
+ const char* frag_shader, bool separable_program) {
+ OGLShader vert, geo, frag;
+ if (vert_shader)
+ vert.Create(vert_shader, GL_VERTEX_SHADER);
+ if (geo_shader)
+ geo.Create(geo_shader, GL_GEOMETRY_SHADER);
+ if (frag_shader)
+ frag.Create(frag_shader, GL_FRAGMENT_SHADER);
+ Create(separable_program, vert.handle, geo.handle, frag.handle);
+}
+
+void OGLProgram::Release() {
+ if (handle == 0)
+ return;
+ glDeleteProgram(handle);
+ OpenGLState::GetCurState().ResetProgram(handle).Apply();
+ handle = 0;
+}
+
+void OGLPipeline::Create() {
+ if (handle != 0)
+ return;
+ glGenProgramPipelines(1, &handle);
+}
+
+void OGLPipeline::Release() {
+ if (handle == 0)
+ return;
+ glDeleteProgramPipelines(1, &handle);
+ OpenGLState::GetCurState().ResetPipeline(handle).Apply();
+ handle = 0;
+}
+
+void OGLBuffer::Create() {
+ if (handle != 0)
+ return;
+ glGenBuffers(1, &handle);
+}
+
+void OGLBuffer::Release() {
+ if (handle == 0)
+ return;
+ glDeleteBuffers(1, &handle);
+ OpenGLState::GetCurState().ResetBuffer(handle).Apply();
+ handle = 0;
+}
+
+void OGLSync::Create() {
+ if (handle != 0)
+ return;
+ handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+void OGLSync::Release() {
+ if (handle == 0)
+ return;
+ glDeleteSync(handle);
+ handle = 0;
+}
+
+void OGLVertexArray::Create() {
+ if (handle != 0)
+ return;
+ glGenVertexArrays(1, &handle);
+}
+
+void OGLVertexArray::Release() {
+ if (handle == 0)
+ return;
+ glDeleteVertexArrays(1, &handle);
+ OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
+ handle = 0;
+}
+
+void OGLFramebuffer::Create() {
+ if (handle != 0)
+ return;
+ glGenFramebuffers(1, &handle);
+}
+
+void OGLFramebuffer::Release() {
+ if (handle == 0)
+ return;
+ glDeleteFramebuffers(1, &handle);
+ OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
+ handle = 0;
+}
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 3bc1b83b5..e33f1e973 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -8,7 +8,6 @@
#include <glad/glad.h>
#include "common/common_types.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
-#include "video_core/renderer_opengl/gl_state.h"
namespace OpenGL {
@@ -29,20 +28,10 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
- void Create() {
- if (handle != 0)
- return;
- glGenTextures(1, &handle);
- }
+ void Create();
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteTextures(1, &handle);
- OpenGLState::GetCurState().UnbindTexture(handle).Apply();
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
@@ -64,20 +53,10 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
- void Create() {
- if (handle != 0)
- return;
- glGenSamplers(1, &handle);
- }
+ void Create();
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteSamplers(1, &handle);
- OpenGLState::GetCurState().ResetSampler(handle).Apply();
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
@@ -98,20 +77,9 @@ public:
return *this;
}
- void Create(const char* source, GLenum type) {
- if (handle != 0)
- return;
- if (source == nullptr)
- return;
- handle = GLShader::LoadShader(source, type);
- }
+ void Create(const char* source, GLenum type);
- void Release() {
- if (handle == 0)
- return;
- glDeleteShader(handle);
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
@@ -141,25 +109,10 @@ public:
/// Creates a new internal OpenGL resource and stores the handle
void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader,
- bool separable_program = false) {
- OGLShader vert, geo, frag;
- if (vert_shader)
- vert.Create(vert_shader, GL_VERTEX_SHADER);
- if (geo_shader)
- geo.Create(geo_shader, GL_GEOMETRY_SHADER);
- if (frag_shader)
- frag.Create(frag_shader, GL_FRAGMENT_SHADER);
- Create(separable_program, vert.handle, geo.handle, frag.handle);
- }
+ bool separable_program = false);
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteProgram(handle);
- OpenGLState::GetCurState().ResetProgram(handle).Apply();
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
@@ -178,20 +131,10 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
- void Create() {
- if (handle != 0)
- return;
- glGenProgramPipelines(1, &handle);
- }
+ void Create();
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteProgramPipelines(1, &handle);
- OpenGLState::GetCurState().ResetPipeline(handle).Apply();
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
@@ -213,20 +156,10 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
- void Create() {
- if (handle != 0)
- return;
- glGenBuffers(1, &handle);
- }
+ void Create();
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteBuffers(1, &handle);
- OpenGLState::GetCurState().ResetBuffer(handle).Apply();
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
@@ -247,19 +180,10 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
- void Create() {
- if (handle != 0)
- return;
- handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
- }
+ void Create();
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteSync(handle);
- handle = 0;
- }
+ void Release();
GLsync handle = 0;
};
@@ -281,20 +205,10 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
- void Create() {
- if (handle != 0)
- return;
- glGenVertexArrays(1, &handle);
- }
+ void Create();
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteVertexArrays(1, &handle);
- OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
@@ -316,20 +230,10 @@ public:
}
/// Creates a new internal OpenGL resource and stores the handle
- void Create() {
- if (handle != 0)
- return;
- glGenFramebuffers(1, &handle);
- }
+ void Create();
/// Deletes the internal OpenGL resource
- void Release() {
- if (handle == 0)
- return;
- glDeleteFramebuffers(1, &handle);
- OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
- handle = 0;
- }
+ void Release();
GLuint handle = 0;
};
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 36fe1f04c..2a069cdd8 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -7,6 +7,7 @@
#include <glad/glad.h>
#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/maxwell_to_gl.h"
namespace OpenGL::GLShader {
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index d9a97e30b..051ad3964 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -19,6 +19,8 @@ SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_t
return SurfaceTarget::Texture3D;
case Tegra::Texture::TextureType::TextureCubemap:
return SurfaceTarget::TextureCubemap;
+ case Tegra::Texture::TextureType::TextureCubeArray:
+ return SurfaceTarget::TextureCubeArray;
case Tegra::Texture::TextureType::Texture1DArray:
return SurfaceTarget::Texture1DArray;
case Tegra::Texture::TextureType::Texture2DArray:
@@ -39,6 +41,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap:
+ case SurfaceTarget::TextureCubeArray:
return true;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
@@ -297,6 +300,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
+ case Tegra::Texture::TextureFormat::ASTC_2D_5X5:
+ return is_srgb ? PixelFormat::ASTC_2D_5X5_SRGB : PixelFormat::ASTC_2D_5X5;
case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
@@ -440,10 +445,12 @@ bool IsPixelFormatASTC(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_5X4:
+ case PixelFormat::ASTC_2D_5X5:
case PixelFormat::ASTC_2D_8X8:
case PixelFormat::ASTC_2D_8X5:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB:
+ case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
return true;
@@ -453,27 +460,7 @@ bool IsPixelFormatASTC(PixelFormat format) {
}
std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
- switch (format) {
- case PixelFormat::ASTC_2D_4X4:
- return {4, 4};
- case PixelFormat::ASTC_2D_5X4:
- return {5, 4};
- case PixelFormat::ASTC_2D_8X8:
- return {8, 8};
- case PixelFormat::ASTC_2D_8X5:
- return {8, 5};
- case PixelFormat::ASTC_2D_4X4_SRGB:
- return {4, 4};
- case PixelFormat::ASTC_2D_5X4_SRGB:
- return {5, 4};
- case PixelFormat::ASTC_2D_8X8_SRGB:
- return {8, 8};
- case PixelFormat::ASTC_2D_8X5_SRGB:
- return {8, 5};
- default:
- LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
- UNREACHABLE();
- }
+ return {GetDefaultBlockWidth(format), GetDefaultBlockHeight(format)};
}
bool IsFormatBCn(PixelFormat format) {
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 3232e437f..dfdb8d122 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -72,19 +72,21 @@ enum class PixelFormat {
ASTC_2D_8X8_SRGB = 54,
ASTC_2D_8X5_SRGB = 55,
ASTC_2D_5X4_SRGB = 56,
+ ASTC_2D_5X5 = 57,
+ ASTC_2D_5X5_SRGB = 58,
MaxColorFormat,
// Depth formats
- Z32F = 57,
- Z16 = 58,
+ Z32F = 59,
+ Z16 = 60,
MaxDepthFormat,
// DepthStencil formats
- Z24S8 = 59,
- S8Z24 = 60,
- Z32FS8 = 61,
+ Z24S8 = 61,
+ S8Z24 = 62,
+ Z32FS8 = 63,
MaxDepthStencilFormat,
@@ -118,6 +120,7 @@ enum class SurfaceTarget {
Texture1DArray,
Texture2DArray,
TextureCubemap,
+ TextureCubeArray,
};
/**
@@ -188,6 +191,8 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
4, // ASTC_2D_8X8_SRGB
4, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB
+ 4, // ASTC_2D_5X5
+ 4, // ASTC_2D_5X5_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
@@ -199,6 +204,79 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
return compression_factor_table[static_cast<std::size_t>(format)];
}
+static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
+ if (format == PixelFormat::Invalid)
+ return 0;
+ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
+ 1, // ABGR8U
+ 1, // ABGR8S
+ 1, // ABGR8UI
+ 1, // B5G6R5U
+ 1, // A2B10G10R10U
+ 1, // A1B5G5R5U
+ 1, // R8U
+ 1, // R8UI
+ 1, // RGBA16F
+ 1, // RGBA16U
+ 1, // RGBA16UI
+ 1, // R11FG11FB10F
+ 1, // RGBA32UI
+ 4, // DXT1
+ 4, // DXT23
+ 4, // DXT45
+ 4, // DXN1
+ 4, // DXN2UNORM
+ 4, // DXN2SNORM
+ 4, // BC7U
+ 4, // BC6H_UF16
+ 4, // BC6H_SF16
+ 4, // ASTC_2D_4X4
+ 1, // G8R8U
+ 1, // G8R8S
+ 1, // BGRA8
+ 1, // RGBA32F
+ 1, // RG32F
+ 1, // R32F
+ 1, // R16F
+ 1, // R16U
+ 1, // R16S
+ 1, // R16UI
+ 1, // R16I
+ 1, // RG16
+ 1, // RG16F
+ 1, // RG16UI
+ 1, // RG16I
+ 1, // RG16S
+ 1, // RGB32F
+ 1, // RGBA8_SRGB
+ 1, // RG8U
+ 1, // RG8S
+ 1, // RG32UI
+ 1, // R32UI
+ 8, // ASTC_2D_8X8
+ 8, // ASTC_2D_8X5
+ 5, // ASTC_2D_5X4
+ 1, // BGRA8_SRGB
+ 4, // DXT1_SRGB
+ 4, // DXT23_SRGB
+ 4, // DXT45_SRGB
+ 4, // BC7U_SRGB
+ 4, // ASTC_2D_4X4_SRGB
+ 8, // ASTC_2D_8X8_SRGB
+ 8, // ASTC_2D_8X5_SRGB
+ 5, // ASTC_2D_5X4_SRGB
+ 5, // ASTC_2D_5X5
+ 5, // ASTC_2D_5X5_SRGB
+ 1, // Z32F
+ 1, // Z16
+ 1, // Z24S8
+ 1, // S8Z24
+ 1, // Z32FS8
+ }};
+ ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
+ return block_width_table[static_cast<std::size_t>(format)];
+}
+
static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
if (format == PixelFormat::Invalid)
return 0;
@@ -261,6 +339,8 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
8, // ASTC_2D_8X8_SRGB
5, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB
+ 5, // ASTC_2D_5X5
+ 5, // ASTC_2D_5X5_SRGB
1, // Z32F
1, // Z16
1, // Z24S8
@@ -299,7 +379,7 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
128, // BC7U
128, // BC6H_UF16
128, // BC6H_SF16
- 32, // ASTC_2D_4X4
+ 128, // ASTC_2D_4X4
16, // G8R8U
16, // G8R8S
32, // BGRA8
@@ -322,18 +402,20 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
16, // RG8S
64, // RG32UI
32, // R32UI
- 16, // ASTC_2D_8X8
- 16, // ASTC_2D_8X5
- 32, // ASTC_2D_5X4
+ 128, // ASTC_2D_8X8
+ 128, // ASTC_2D_8X5
+ 128, // ASTC_2D_5X4
32, // BGRA8_SRGB
64, // DXT1_SRGB
128, // DXT23_SRGB
128, // DXT45_SRGB
128, // BC7U
- 32, // ASTC_2D_4X4_SRGB
- 16, // ASTC_2D_8X8_SRGB
- 16, // ASTC_2D_8X5_SRGB
- 32, // ASTC_2D_5X4_SRGB
+ 128, // ASTC_2D_4X4_SRGB
+ 128, // ASTC_2D_8X8_SRGB
+ 128, // ASTC_2D_8X5_SRGB
+ 128, // ASTC_2D_5X4_SRGB
+ 128, // ASTC_2D_5X5
+ 128, // ASTC_2D_5X5_SRGB
32, // Z32F
16, // Z16
32, // Z24S8
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index b1feacae9..bc50a4876 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1598,27 +1598,29 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
namespace Tegra::Texture::ASTC {
std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
- uint32_t block_width, uint32_t block_height) {
+ uint32_t depth, uint32_t block_width, uint32_t block_height) {
uint32_t blockIdx = 0;
- std::vector<uint8_t> outData(height * width * 4);
- for (uint32_t j = 0; j < height; j += block_height) {
- for (uint32_t i = 0; i < width; i += block_width) {
+ std::vector<uint8_t> outData(height * width * depth * 4);
+ for (uint32_t k = 0; k < depth; k++) {
+ for (uint32_t j = 0; j < height; j += block_height) {
+ for (uint32_t i = 0; i < width; i += block_width) {
- uint8_t* blockPtr = data.data() + blockIdx * 16;
+ uint8_t* blockPtr = data.data() + blockIdx * 16;
- // Blocks can be at most 12x12
- uint32_t uncompData[144];
- ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
+ // Blocks can be at most 12x12
+ uint32_t uncompData[144];
+ ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
- uint32_t decompWidth = std::min(block_width, width - i);
- uint32_t decompHeight = std::min(block_height, height - j);
+ uint32_t decompWidth = std::min(block_width, width - i);
+ uint32_t decompHeight = std::min(block_height, height - j);
- uint8_t* outRow = outData.data() + (j * width + i) * 4;
- for (uint32_t jj = 0; jj < decompHeight; jj++) {
- memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
- }
+ uint8_t* outRow = outData.data() + (j * width + i) * 4;
+ for (uint32_t jj = 0; jj < decompHeight; jj++) {
+ memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
+ }
- blockIdx++;
+ blockIdx++;
+ }
}
}
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index f0d7c0e56..d419dd025 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -10,6 +10,6 @@
namespace Tegra::Texture::ASTC {
std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
- uint32_t block_width, uint32_t block_height);
+ uint32_t depth, uint32_t block_width, uint32_t block_height);
} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 550ca856c..3066abf61 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -227,12 +227,14 @@ u32 BytesPerPixel(TextureFormat format) {
}
}
-std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
- u32 height, u32 depth, u32 block_height, u32 block_depth) {
+std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
+ u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
+ u32 block_height, u32 block_depth) {
std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
- CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel,
- Memory::GetPointer(address), unswizzled_data.data(), true, block_height,
- block_depth);
+ CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
+ (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
+ bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
+ block_height, block_depth);
return unswizzled_data;
}
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index b390219e4..ba065510b 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -19,8 +19,8 @@ inline std::size_t GetGOBSize() {
/**
* Unswizzles a swizzled texture without changing its format.
*/
-std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
- u32 height, u32 depth,
+std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
+ u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height = TICEntry::DefaultBlockHeight,
u32 block_depth = TICEntry::DefaultBlockHeight);
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 0adbab27d..707747422 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,9 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
// Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
- auto unswizzled_data =
- Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format),
- surface_width, surface_height, 1U);
+ auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
+ *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width,
+ surface_height, 1U);
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
surface_width, surface_height);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c5a56cbfd..74a44be37 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -142,6 +142,9 @@ static void InitializeLogging() {
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
+#ifdef _WIN32
+ Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
+#endif
}
GMainWindow::GMainWindow()
@@ -454,6 +457,7 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help
+ connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
connect(ui.action_Rederive, &QAction::triggered, this,
std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
@@ -1374,6 +1378,11 @@ void GMainWindow::OnLoadAmiibo() {
}
}
+void GMainWindow::OnOpenYuzuFolder() {
+ QDesktopServices::openUrl(QUrl::fromLocalFile(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
+}
+
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
@@ -1532,7 +1541,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
"derivation. It will be attempted but may not complete.<br><br>") +
errors +
tr("<br><br>You can get all of these and dump all of your games easily by "
- "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
+ "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
"quickstart guide</a>. Alternatively, you can use another method of dumping "
"to obtain all of your keys."));
}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index af637d89e..929250e8c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -167,6 +167,7 @@ private slots:
void OnMenuRecentFile();
void OnConfigure();
void OnLoadAmiibo();
+ void OnOpenYuzuFolder();
void OnAbout();
void OnToggleFilterBar();
void OnDisplayTitleBars(bool);
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 48d099591..28cf269e7 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -110,6 +110,7 @@
<string>&amp;Help</string>
</property>
<addaction name="action_Report_Compatibility"/>
+ <addaction name="action_Open_yuzu_Folder" />
<addaction name="separator"/>
<addaction name="action_About"/>
</widget>
@@ -277,6 +278,11 @@
<bool>false</bool>
</property>
</action>
+ <action name="action_Open_yuzu_Folder">
+ <property name="text">
+ <string>Open yuzu Folder</string>
+ </property>
+ </action>
</widget>
<resources/>
<connections/>
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c8b93b85b..806127b12 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -76,6 +76,9 @@ static void InitializeLogging() {
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
+#ifdef _WIN32
+ Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
+#endif
}
/// Application entry point