Merge branch 'master' into Texture2DArray

This commit is contained in:
greggameplayer 2018-11-06 19:05:57 +01:00 committed by GitHub
commit d3b9599b2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 392 additions and 195 deletions

View File

@ -13,6 +13,7 @@
#include <vector> #include <vector>
#ifdef _WIN32 #ifdef _WIN32
#include <share.h> // For _SH_DENYWR #include <share.h> // For _SH_DENYWR
#include <windows.h> // For OutputDebugStringA
#else #else
#define _SH_DENYWR 0 #define _SH_DENYWR 0
#endif #endif
@ -139,12 +140,18 @@ void FileBackend::Write(const Entry& entry) {
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
return; return;
} }
bytes_written += file.WriteString(FormatLogMessage(entry) + '\n'); bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) { if (entry.log_level >= Level::Error) {
file.Flush(); 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. /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \ #define ALL_LOG_CLASSES() \
CLS(Log) \ CLS(Log) \

View File

@ -103,6 +103,20 @@ private:
std::size_t bytes_written; 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 AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(std::string_view backend_name); void RemoveBackend(std::string_view backend_name);

View File

@ -161,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
std::size_t worker_sz = WorkerBufferSize(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{ std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
static_cast<OpusDecoder*>(operator new(worker_sz))}; static_cast<OpusDecoder*>(operator new(worker_sz))};
if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {

View File

@ -427,6 +427,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
} }
Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { 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; return styleset_changed_event;
} }

View File

@ -96,6 +96,8 @@ public:
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
ReloadInputDevices();
} }
void ActivateController(HidController controller) { void ActivateController(HidController controller) {

View File

@ -58,9 +58,9 @@ public:
/// Rotate source image 90 degrees clockwise /// Rotate source image 90 degrees clockwise
Rotate90 = 0x04, Rotate90 = 0x04,
/// Rotate source image 180 degrees /// Rotate source image 180 degrees
Roate180 = 0x03, Rotate180 = 0x03,
/// Rotate source image 270 degrees clockwise /// Rotate source image 270 degrees clockwise
Roate270 = 0x07, Rotate270 = 0x07,
}; };
struct Buffer { struct Buffer {

View File

@ -33,6 +33,7 @@ add_library(video_core STATIC
renderer_opengl/gl_rasterizer.h renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.cpp renderer_opengl/gl_rasterizer_cache.cpp
renderer_opengl/gl_rasterizer_cache.h renderer_opengl/gl_rasterizer_cache.h
renderer_opengl/gl_resource_manager.cpp
renderer_opengl/gl_resource_manager.h renderer_opengl/gl_resource_manager.h
renderer_opengl/gl_shader_cache.cpp renderer_opengl/gl_shader_cache.cpp
renderer_opengl/gl_shader_cache.h renderer_opengl/gl_shader_cache.h

View File

@ -16,6 +16,7 @@
#include "core/settings.h" #include "core/settings.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.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/renderer_opengl/utils.h"
#include "video_core/surface.h" #include "video_core/surface.h"
#include "video_core/textures/astc.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, std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only,
bool uncompressed) const { 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)}; const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
u32 m_depth = (layer_only ? 1U : depth); u32 m_depth = (layer_only ? 1U : depth);
u32 m_width = MipWidth(mip_level); u32 m_width = MipWidth(mip_level);
u32 m_height = MipHeight(mip_level); u32 m_height = MipHeight(mip_level);
m_width = uncompressed ? m_width m_width = uncompressed ? m_width : std::max(1U, (m_width + tile_x - 1) / tile_x);
: std::max(1U, (m_width + compression_factor - 1) / compression_factor); m_height = uncompressed ? m_height : std::max(1U, (m_height + tile_y - 1) / tile_y);
m_height = uncompressed
? m_height
: std::max(1U, (m_height + compression_factor - 1) / compression_factor);
m_depth = std::max(1U, m_depth >> mip_level); m_depth = std::max(1U, m_depth >> mip_level);
u32 m_block_height = MipBlockHeight(mip_level); u32 m_block_height = MipBlockHeight(mip_level);
u32 m_block_depth = MipBlockDepth(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; params.target = SurfaceTarget::Texture2D;
} }
break; break;
case SurfaceTarget::TextureCubeArray:
params.depth = config.tic.Depth() * 6;
if (!entry.IsArray()) {
ASSERT(params.depth == 6);
params.target = SurfaceTarget::TextureCubemap;
}
break;
default: default:
LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target)); LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target));
UNREACHABLE(); 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_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_8X5_SRGB
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_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 // Depth formats
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F {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; return GL_TEXTURE_2D_ARRAY;
case SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
return GL_TEXTURE_CUBE_MAP; 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)); LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
UNREACHABLE(); 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 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
// pixel values. // 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) { if (morton_to_gl) {
const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( const std::vector<u8> data =
addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth); 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())}; const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
memcpy(gl_buffer, data.data(), size_to_copy); memcpy(gl_buffer, data.data(), size_to_copy);
} else { } 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), bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
gl_buffer, false, block_height, block_depth); 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_8X8_SRGB>,
MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
MortonCopy<true, PixelFormat::ASTC_2D_5X4_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::Z32F>,
MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z16>,
MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::Z24S8>,
@ -508,6 +523,8 @@ static constexpr GLConversionArray gl_to_morton_fns = {
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::Z32F>, MortonCopy<false, PixelFormat::Z32F>,
MortonCopy<false, PixelFormat::Z16>, MortonCopy<false, PixelFormat::Z16>,
MortonCopy<false, PixelFormat::Z24S8>, MortonCopy<false, PixelFormat::Z24S8>,
@ -754,6 +771,7 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
break; break;
case SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height, glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
static_cast<GLsizei>(dst_params.depth), dest_format.format, static_cast<GLsizei>(dst_params.depth), dest_format.format,
dest_format.type, nullptr); dest_format.type, nullptr);
@ -806,6 +824,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
break; break;
case SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level, glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(), format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
params.depth); params.depth);
@ -897,21 +916,24 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
* typical desktop GPUs. * typical desktop GPUs.
*/ */
static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
u32 width, u32 height) { u32 width, u32 height, u32 depth) {
switch (pixel_format) { switch (pixel_format) {
case PixelFormat::ASTC_2D_4X4: case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_8X8:
case PixelFormat::ASTC_2D_8X5: case PixelFormat::ASTC_2D_8X5:
case PixelFormat::ASTC_2D_5X4: case PixelFormat::ASTC_2D_5X4:
case PixelFormat::ASTC_2D_5X5:
case PixelFormat::ASTC_2D_4X4_SRGB: case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB: case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_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. // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
u32 block_width{}; u32 block_width{};
u32 block_height{}; u32 block_height{};
std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format); 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; break;
} }
case PixelFormat::S8Z24: case PixelFormat::S8Z24:
@ -971,7 +993,7 @@ void CachedSurface::LoadGLBuffer() {
} }
for (u32 i = 0; i < params.max_mip_level; i++) for (u32 i = 0; i < params.max_mip_level; i++)
ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(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)); 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]); &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
static_cast<GLsizei>(params.MipWidth(mip_map)), static_cast<GLsizei>(params.MipWidth(mip_map)),
static_cast<GLsizei>(params.MipHeight(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]); tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
break; break;
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, 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::TextureCubemap:
case SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
AccurateCopySurface(old_surface, new_surface); AccurateCopySurface(old_surface, new_surface);
break; break;
default: default:

View File

@ -49,6 +49,8 @@ struct SurfaceParams {
return "Texture2DArray"; return "Texture2DArray";
case SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
return "TextureCubemap"; return "TextureCubemap";
case SurfaceTarget::TextureCubeArray:
return "TextureCubeArray";
default: default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
UNREACHABLE(); UNREACHABLE();
@ -139,7 +141,7 @@ struct SurfaceParams {
} }
u32 MipDepth(u32 mip_level) const { 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: // Auto block resizing algorithm from:

View File

@ -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

View File

@ -8,7 +8,6 @@
#include <glad/glad.h> #include <glad/glad.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/gl_state.h"
namespace OpenGL { namespace OpenGL {
@ -29,20 +28,10 @@ public:
} }
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void Create() { void Create();
if (handle != 0)
return;
glGenTextures(1, &handle);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteTextures(1, &handle);
OpenGLState::GetCurState().UnbindTexture(handle).Apply();
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };
@ -64,20 +53,10 @@ public:
} }
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void Create() { void Create();
if (handle != 0)
return;
glGenSamplers(1, &handle);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteSamplers(1, &handle);
OpenGLState::GetCurState().ResetSampler(handle).Apply();
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };
@ -98,20 +77,9 @@ public:
return *this; return *this;
} }
void Create(const char* source, GLenum type) { void Create(const char* source, GLenum type);
if (handle != 0)
return;
if (source == nullptr)
return;
handle = GLShader::LoadShader(source, type);
}
void Release() { void Release();
if (handle == 0)
return;
glDeleteShader(handle);
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };
@ -141,25 +109,10 @@ public:
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader, void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader,
bool separable_program = false) { 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);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteProgram(handle);
OpenGLState::GetCurState().ResetProgram(handle).Apply();
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };
@ -178,20 +131,10 @@ public:
} }
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void Create() { void Create();
if (handle != 0)
return;
glGenProgramPipelines(1, &handle);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteProgramPipelines(1, &handle);
OpenGLState::GetCurState().ResetPipeline(handle).Apply();
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };
@ -213,20 +156,10 @@ public:
} }
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void Create() { void Create();
if (handle != 0)
return;
glGenBuffers(1, &handle);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteBuffers(1, &handle);
OpenGLState::GetCurState().ResetBuffer(handle).Apply();
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };
@ -247,19 +180,10 @@ public:
} }
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void Create() { void Create();
if (handle != 0)
return;
handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteSync(handle);
handle = 0;
}
GLsync handle = 0; GLsync handle = 0;
}; };
@ -281,20 +205,10 @@ public:
} }
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void Create() { void Create();
if (handle != 0)
return;
glGenVertexArrays(1, &handle);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteVertexArrays(1, &handle);
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };
@ -316,20 +230,10 @@ public:
} }
/// Creates a new internal OpenGL resource and stores the handle /// Creates a new internal OpenGL resource and stores the handle
void Create() { void Create();
if (handle != 0)
return;
glGenFramebuffers(1, &handle);
}
/// Deletes the internal OpenGL resource /// Deletes the internal OpenGL resource
void Release() { void Release();
if (handle == 0)
return;
glDeleteFramebuffers(1, &handle);
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
handle = 0;
}
GLuint handle = 0; GLuint handle = 0;
}; };

View File

@ -7,6 +7,7 @@
#include <glad/glad.h> #include <glad/glad.h>
#include "video_core/renderer_opengl/gl_resource_manager.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" #include "video_core/renderer_opengl/maxwell_to_gl.h"
namespace OpenGL::GLShader { namespace OpenGL::GLShader {

View File

@ -19,6 +19,8 @@ SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_t
return SurfaceTarget::Texture3D; return SurfaceTarget::Texture3D;
case Tegra::Texture::TextureType::TextureCubemap: case Tegra::Texture::TextureType::TextureCubemap:
return SurfaceTarget::TextureCubemap; return SurfaceTarget::TextureCubemap;
case Tegra::Texture::TextureType::TextureCubeArray:
return SurfaceTarget::TextureCubeArray;
case Tegra::Texture::TextureType::Texture1DArray: case Tegra::Texture::TextureType::Texture1DArray:
return SurfaceTarget::Texture1DArray; return SurfaceTarget::Texture1DArray;
case Tegra::Texture::TextureType::Texture2DArray: case Tegra::Texture::TextureType::Texture2DArray:
@ -39,6 +41,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
case SurfaceTarget::Texture1DArray: case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
case SurfaceTarget::TextureCubeArray:
return true; return true;
default: default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); 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; return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
case Tegra::Texture::TextureFormat::ASTC_2D_5X4: case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::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: case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
case Tegra::Texture::TextureFormat::ASTC_2D_8X5: case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
@ -440,10 +445,12 @@ bool IsPixelFormatASTC(PixelFormat format) {
switch (format) { switch (format) {
case PixelFormat::ASTC_2D_4X4: case PixelFormat::ASTC_2D_4X4:
case PixelFormat::ASTC_2D_5X4: case PixelFormat::ASTC_2D_5X4:
case PixelFormat::ASTC_2D_5X5:
case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_8X8:
case PixelFormat::ASTC_2D_8X5: case PixelFormat::ASTC_2D_8X5:
case PixelFormat::ASTC_2D_4X4_SRGB: case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_5X4_SRGB: case PixelFormat::ASTC_2D_5X4_SRGB:
case PixelFormat::ASTC_2D_5X5_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB: case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB: case PixelFormat::ASTC_2D_8X5_SRGB:
return true; return true;
@ -453,27 +460,7 @@ bool IsPixelFormatASTC(PixelFormat format) {
} }
std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
switch (format) { return {GetDefaultBlockWidth(format), GetDefaultBlockHeight(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();
}
} }
bool IsFormatBCn(PixelFormat format) { bool IsFormatBCn(PixelFormat format) {

View File

@ -72,19 +72,21 @@ enum class PixelFormat {
ASTC_2D_8X8_SRGB = 54, ASTC_2D_8X8_SRGB = 54,
ASTC_2D_8X5_SRGB = 55, ASTC_2D_8X5_SRGB = 55,
ASTC_2D_5X4_SRGB = 56, ASTC_2D_5X4_SRGB = 56,
ASTC_2D_5X5 = 57,
ASTC_2D_5X5_SRGB = 58,
MaxColorFormat, MaxColorFormat,
// Depth formats // Depth formats
Z32F = 57, Z32F = 59,
Z16 = 58, Z16 = 60,
MaxDepthFormat, MaxDepthFormat,
// DepthStencil formats // DepthStencil formats
Z24S8 = 59, Z24S8 = 61,
S8Z24 = 60, S8Z24 = 62,
Z32FS8 = 61, Z32FS8 = 63,
MaxDepthStencilFormat, MaxDepthStencilFormat,
@ -118,6 +120,7 @@ enum class SurfaceTarget {
Texture1DArray, Texture1DArray,
Texture2DArray, Texture2DArray,
TextureCubemap, TextureCubemap,
TextureCubeArray,
}; };
/** /**
@ -188,6 +191,8 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
4, // ASTC_2D_8X8_SRGB 4, // ASTC_2D_8X8_SRGB
4, // ASTC_2D_8X5_SRGB 4, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB 4, // ASTC_2D_5X4_SRGB
4, // ASTC_2D_5X5
4, // ASTC_2D_5X5_SRGB
1, // Z32F 1, // Z32F
1, // Z16 1, // Z16
1, // Z24S8 1, // Z24S8
@ -199,6 +204,79 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
return compression_factor_table[static_cast<std::size_t>(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) { static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
if (format == PixelFormat::Invalid) if (format == PixelFormat::Invalid)
return 0; return 0;
@ -261,6 +339,8 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
8, // ASTC_2D_8X8_SRGB 8, // ASTC_2D_8X8_SRGB
5, // ASTC_2D_8X5_SRGB 5, // ASTC_2D_8X5_SRGB
4, // ASTC_2D_5X4_SRGB 4, // ASTC_2D_5X4_SRGB
5, // ASTC_2D_5X5
5, // ASTC_2D_5X5_SRGB
1, // Z32F 1, // Z32F
1, // Z16 1, // Z16
1, // Z24S8 1, // Z24S8
@ -299,7 +379,7 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
128, // BC7U 128, // BC7U
128, // BC6H_UF16 128, // BC6H_UF16
128, // BC6H_SF16 128, // BC6H_SF16
32, // ASTC_2D_4X4 128, // ASTC_2D_4X4
16, // G8R8U 16, // G8R8U
16, // G8R8S 16, // G8R8S
32, // BGRA8 32, // BGRA8
@ -322,18 +402,20 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
16, // RG8S 16, // RG8S
64, // RG32UI 64, // RG32UI
32, // R32UI 32, // R32UI
16, // ASTC_2D_8X8 128, // ASTC_2D_8X8
16, // ASTC_2D_8X5 128, // ASTC_2D_8X5
32, // ASTC_2D_5X4 128, // ASTC_2D_5X4
32, // BGRA8_SRGB 32, // BGRA8_SRGB
64, // DXT1_SRGB 64, // DXT1_SRGB
128, // DXT23_SRGB 128, // DXT23_SRGB
128, // DXT45_SRGB 128, // DXT45_SRGB
128, // BC7U 128, // BC7U
32, // ASTC_2D_4X4_SRGB 128, // ASTC_2D_4X4_SRGB
16, // ASTC_2D_8X8_SRGB 128, // ASTC_2D_8X8_SRGB
16, // ASTC_2D_8X5_SRGB 128, // ASTC_2D_8X5_SRGB
32, // ASTC_2D_5X4_SRGB 128, // ASTC_2D_5X4_SRGB
128, // ASTC_2D_5X5
128, // ASTC_2D_5X5_SRGB
32, // Z32F 32, // Z32F
16, // Z16 16, // Z16
32, // Z24S8 32, // Z24S8

View File

@ -1598,9 +1598,10 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
namespace Tegra::Texture::ASTC { namespace Tegra::Texture::ASTC {
std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 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; uint32_t blockIdx = 0;
std::vector<uint8_t> outData(height * width * 4); 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 j = 0; j < height; j += block_height) {
for (uint32_t i = 0; i < width; i += block_width) { for (uint32_t i = 0; i < width; i += block_width) {
@ -1621,6 +1622,7 @@ std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint
blockIdx++; blockIdx++;
} }
} }
}
return outData; return outData;
} }

View File

@ -10,6 +10,6 @@
namespace Tegra::Texture::ASTC { namespace Tegra::Texture::ASTC {
std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 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 } // namespace Tegra::Texture::ASTC

View File

@ -227,12 +227,14 @@ u32 BytesPerPixel(TextureFormat format) {
} }
} }
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 height, u32 depth, u32 block_height, u32 block_depth) { 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); 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, CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
Memory::GetPointer(address), unswizzled_data.data(), true, block_height, (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
block_depth); bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
block_height, block_depth);
return unswizzled_data; return unswizzled_data;
} }

View File

@ -19,8 +19,8 @@ inline std::size_t GetGOBSize() {
/** /**
* Unswizzles a swizzled texture without changing its format. * Unswizzles a swizzled texture without changing its format.
*/ */
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 height, u32 depth, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height = TICEntry::DefaultBlockHeight, u32 block_height = TICEntry::DefaultBlockHeight,
u32 block_depth = TICEntry::DefaultBlockHeight); u32 block_depth = TICEntry::DefaultBlockHeight);

View File

@ -386,9 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. // 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. // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
auto unswizzled_data = auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format), *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width,
surface_width, surface_height, 1U); surface_height, 1U);
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
surface_width, surface_height); surface_width, surface_height);

View File

@ -142,6 +142,9 @@ static void InitializeLogging() {
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir); FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
#endif
} }
GMainWindow::GMainWindow() GMainWindow::GMainWindow()
@ -454,6 +457,7 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help // Help
connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
connect(ui.action_Rederive, &QAction::triggered, this, connect(ui.action_Rederive, &QAction::triggered, this,
std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 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() { void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this); AboutDialog aboutDialog(this);
aboutDialog.exec(); aboutDialog.exec();
@ -1532,7 +1541,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
"derivation. It will be attempted but may not complete.<br><br>") + "derivation. It will be attempted but may not complete.<br><br>") +
errors + errors +
tr("<br><br>You can get all of these and dump all of your games easily by " 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 " "quickstart guide</a>. Alternatively, you can use another method of dumping "
"to obtain all of your keys.")); "to obtain all of your keys."));
} }

View File

@ -167,6 +167,7 @@ private slots:
void OnMenuRecentFile(); void OnMenuRecentFile();
void OnConfigure(); void OnConfigure();
void OnLoadAmiibo(); void OnLoadAmiibo();
void OnOpenYuzuFolder();
void OnAbout(); void OnAbout();
void OnToggleFilterBar(); void OnToggleFilterBar();
void OnDisplayTitleBars(bool); void OnDisplayTitleBars(bool);

View File

@ -110,6 +110,7 @@
<string>&amp;Help</string> <string>&amp;Help</string>
</property> </property>
<addaction name="action_Report_Compatibility"/> <addaction name="action_Report_Compatibility"/>
<addaction name="action_Open_yuzu_Folder" />
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_About"/> <addaction name="action_About"/>
</widget> </widget>
@ -277,6 +278,11 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
</action> </action>
<action name="action_Open_yuzu_Folder">
<property name="text">
<string>Open yuzu Folder</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -76,6 +76,9 @@ static void InitializeLogging() {
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir); FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
#endif
} }
/// Application entry point /// Application entry point