mirror of
https://github.com/PabloMK7/citra.git
synced 2025-09-11 13:20:04 +00:00
Prepare frontend for multiple graphics APIs (#6347)
* externals: Update dynarmic * settings: Introduce GraphicsAPI enum * For now it's OpenGL only but will be expanded upon later * citra_qt: Introduce backend agnostic context management * Mostly a direct port from yuzu * core: Simplify context acquire * settings: Add option to create debug contexts * renderer_opengl: Abstract initialization to Driver * This commit also updates glad and adds some useful extensions which we will use in part 2 * Rasterizer construction is moved to the specific renderer instead of RendererBase. Software rendering has been disable to achieve this but will be brought back in the next commit. * video_core: Remove Init/Shutdown methods from renderer * The constructor and destructor can do the same job * In addition move opengl function loading to Qt since SDL already does this. Also remove ErrorVideoCore which is never reached * citra_qt: Decouple software renderer from opengl part 1 * citra: Decouple software renderer from opengl part 2 * android: Decouple software renderer from opengl part 3 * swrasterizer: Decouple software renderer from opengl part 4 * This commit simply enforces the renderer naming conventions in the software renderer * video_core: Move RendererBase to VideoCore * video_core: De-globalize screenshot state * video_core: Pass system to the renderers * video_core: Commonize shader uniform data * video_core: Abstract backend agnostic rasterizer operations * bootmanager: Remove references to OpenGL for macOS OpenGL macOS headers definitions clash heavily with each other * citra_qt: Proper title for api settings * video_core: Reduce boost usage * bootmanager: Fix hide mouse option Remove event handlers from RenderWidget for events that are already handled by the parent GRenderWindow. Also enable mouse tracking on the RenderWidget. * android: Remove software from graphics api list * code: Address review comments * citra: Port per-game settings read * Having to update the default value for all backends is a pain so lets centralize it * android: Rename to OpenGLES --------- Co-authored-by: MerryMage <MerryMage@users.noreply.github.com> Co-authored-by: Vitor Kiguchi <vitor-kiguchi@hotmail.com>
This commit is contained in:
parent
9ef42040af
commit
b5d6f645bd
99 changed files with 3165 additions and 4501 deletions
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include <glad/glad.h>
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "video_core/renderer_opengl/frame_dumper_opengl.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
|
||||
|
@ -39,7 +38,7 @@ void FrameDumperOpenGL::StopDumping() {
|
|||
}
|
||||
|
||||
void FrameDumperOpenGL::PresentLoop() {
|
||||
Frontend::ScopeAcquireContext scope{*context};
|
||||
const auto scope = context->Acquire();
|
||||
InitializeOpenGLObjects();
|
||||
|
||||
const auto& layout = GetLayout();
|
||||
|
|
156
src/video_core/renderer_opengl/gl_driver.cpp
Normal file
156
src/video_core/renderer_opengl/gl_driver.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
DECLARE_ENUM_FLAG_OPERATORS(DriverBug);
|
||||
|
||||
inline std::string_view GetSource(GLenum source) {
|
||||
#define RET(s) \
|
||||
case GL_DEBUG_SOURCE_##s: \
|
||||
return #s
|
||||
switch (source) {
|
||||
RET(API);
|
||||
RET(WINDOW_SYSTEM);
|
||||
RET(SHADER_COMPILER);
|
||||
RET(THIRD_PARTY);
|
||||
RET(APPLICATION);
|
||||
RET(OTHER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
#undef RET
|
||||
|
||||
return std::string_view{};
|
||||
}
|
||||
|
||||
inline std::string_view GetType(GLenum type) {
|
||||
#define RET(t) \
|
||||
case GL_DEBUG_TYPE_##t: \
|
||||
return #t
|
||||
switch (type) {
|
||||
RET(ERROR);
|
||||
RET(DEPRECATED_BEHAVIOR);
|
||||
RET(UNDEFINED_BEHAVIOR);
|
||||
RET(PORTABILITY);
|
||||
RET(PERFORMANCE);
|
||||
RET(OTHER);
|
||||
RET(MARKER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
#undef RET
|
||||
|
||||
return std::string_view{};
|
||||
}
|
||||
|
||||
static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar* message, const void* user_param) {
|
||||
Log::Level level = Log::Level::Info;
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
level = Log::Level::Critical;
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
level = Log::Level::Warning;
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
level = Log::Level::Debug;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type),
|
||||
id, message);
|
||||
}
|
||||
|
||||
Driver::Driver(Core::TelemetrySession& telemetry_session_) : telemetry_session{telemetry_session_} {
|
||||
const bool enable_debug = Settings::values.renderer_debug.GetValue();
|
||||
if (enable_debug) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageCallback(DebugHandler, nullptr);
|
||||
}
|
||||
|
||||
ReportDriverInfo();
|
||||
DeduceVendor();
|
||||
CheckExtensionSupport();
|
||||
FindBugs();
|
||||
}
|
||||
|
||||
Driver::~Driver() = default;
|
||||
|
||||
bool Driver::HasBug(DriverBug bug) const {
|
||||
return True(bugs & bug);
|
||||
}
|
||||
|
||||
void Driver::ReportDriverInfo() {
|
||||
// Report the context version and the vendor string
|
||||
gl_version = std::string_view{reinterpret_cast<const char*>(glGetString(GL_VERSION))};
|
||||
gpu_vendor = std::string_view{reinterpret_cast<const char*>(glGetString(GL_VENDOR))};
|
||||
gpu_model = std::string_view{reinterpret_cast<const char*>(glGetString(GL_RENDERER))};
|
||||
|
||||
LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
|
||||
LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
|
||||
LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
|
||||
|
||||
// Add the information to the telemetry system
|
||||
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
|
||||
telemetry_session.AddField(user_system, "GPU_Vendor", std::string{gpu_vendor});
|
||||
telemetry_session.AddField(user_system, "GPU_Model", std::string{gpu_model});
|
||||
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string{gl_version});
|
||||
}
|
||||
|
||||
void Driver::DeduceVendor() {
|
||||
if (gpu_vendor.find("NVIDIA") != gpu_vendor.npos) {
|
||||
vendor = Vendor::Nvidia;
|
||||
} else if ((gpu_vendor.find("ATI") != gpu_vendor.npos) ||
|
||||
(gpu_vendor.find("AMD") != gpu_vendor.npos) ||
|
||||
(gpu_vendor.find("Advanced Micro Devices") != gpu_vendor.npos)) {
|
||||
vendor = Vendor::AMD;
|
||||
} else if (gpu_vendor.find("Intel") != gpu_vendor.npos) {
|
||||
vendor = Vendor::Intel;
|
||||
} else if (gpu_vendor.find("ARM") != gpu_vendor.npos) {
|
||||
vendor = Vendor::ARM;
|
||||
} else if (gpu_vendor.find("Qualcomm") != gpu_vendor.npos) {
|
||||
vendor = Vendor::Qualcomm;
|
||||
} else if (gpu_vendor.find("Samsung") != gpu_vendor.npos) {
|
||||
vendor = Vendor::Samsung;
|
||||
} else if (gpu_vendor.find("GDI Generic") != gpu_vendor.npos) {
|
||||
vendor = Vendor::Generic;
|
||||
}
|
||||
}
|
||||
|
||||
void Driver::CheckExtensionSupport() {
|
||||
ext_buffer_storage = GLAD_GL_EXT_buffer_storage;
|
||||
arb_buffer_storage = GLAD_GL_ARB_buffer_storage;
|
||||
arb_clear_texture = GLAD_GL_ARB_clear_texture;
|
||||
arb_get_texture_sub_image = GLAD_GL_ARB_get_texture_sub_image;
|
||||
ext_clip_cull_distance = GLAD_GL_EXT_clip_cull_distance;
|
||||
is_suitable = GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1;
|
||||
}
|
||||
|
||||
void Driver::FindBugs() {
|
||||
#ifdef __unix__
|
||||
const bool is_linux = true;
|
||||
#else
|
||||
const bool is_linux = false;
|
||||
#endif
|
||||
|
||||
// TODO: Check if these have been fixed in the newer driver
|
||||
if (vendor == Vendor::AMD) {
|
||||
bugs |= DriverBug::ShaderStageChangeFreeze | DriverBug::VertexArrayOutOfBound;
|
||||
}
|
||||
|
||||
if (vendor == Vendor::AMD || (vendor == Vendor::Intel && !is_linux)) {
|
||||
bugs |= DriverBug::BrokenTextureView;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
114
src/video_core/renderer_opengl/gl_driver.h
Normal file
114
src/video_core/renderer_opengl/gl_driver.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class TelemetrySession;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
enum class Vendor {
|
||||
Unknown = 0,
|
||||
AMD = 1,
|
||||
Nvidia = 2,
|
||||
Intel = 3,
|
||||
ARM = 4,
|
||||
Qualcomm = 5,
|
||||
Samsung = 6,
|
||||
Generic = 7,
|
||||
};
|
||||
|
||||
enum class DriverBug {
|
||||
// AMD drivers sometimes freezes when one shader stage is changed but not the others.
|
||||
ShaderStageChangeFreeze = 1 << 0,
|
||||
// On AMD drivers there is a strange crash in indexed drawing. The crash happens when the buffer
|
||||
// read position is near the end and is an out-of-bound access to the vertex buffer. This is
|
||||
// probably a bug in the driver and is related to the usage of vec3<byte> attributes in the
|
||||
// vertex array. Doubling the allocation size for the vertex buffer seems to avoid the crash.
|
||||
VertexArrayOutOfBound = 1 << 1,
|
||||
// On AMD and Intel drivers on Windows glTextureView produces incorrect results
|
||||
BrokenTextureView = 1 << 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class that loads the OpenGL function pointers and reports
|
||||
* information about the graphics device and driver used
|
||||
*/
|
||||
class Driver {
|
||||
public:
|
||||
Driver(Core::TelemetrySession& telemetry_session);
|
||||
~Driver();
|
||||
|
||||
/// Returns true of the driver has a particular bug stated in the DriverBug enum
|
||||
bool HasBug(DriverBug bug) const;
|
||||
|
||||
/// Returns the vendor of the currently selected physical device
|
||||
Vendor GetVendor() const {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
/// Returns the gpu vendor string returned by the driver
|
||||
std::string_view GetVendorString() const {
|
||||
return gpu_vendor;
|
||||
}
|
||||
|
||||
/// Returns true if the implementation is suitable for emulation
|
||||
bool IsSuitable() const {
|
||||
return is_suitable;
|
||||
}
|
||||
|
||||
/// Returns true if the implementation supports ARB_buffer_storage
|
||||
bool HasArbBufferStorage() const {
|
||||
return arb_buffer_storage;
|
||||
}
|
||||
|
||||
/// Returns true if the implementation supports EXT_buffer_storage
|
||||
bool HasExtBufferStorage() const {
|
||||
return ext_buffer_storage;
|
||||
}
|
||||
|
||||
/// Returns true if the implementation supports ARB_clear_texture
|
||||
bool HasArbClearTexture() const {
|
||||
return arb_clear_texture;
|
||||
}
|
||||
|
||||
/// Returns true if the implementation supports ARB_get_texture_sub_image
|
||||
bool HasArbGetTextureSubImage() const {
|
||||
return arb_get_texture_sub_image;
|
||||
}
|
||||
|
||||
/// Returns true if the implementation supports EXT_clip_cull_distance
|
||||
bool HasExtClipCullDistance() const {
|
||||
return ext_clip_cull_distance;
|
||||
}
|
||||
|
||||
private:
|
||||
void ReportDriverInfo();
|
||||
void DeduceVendor();
|
||||
void CheckExtensionSupport();
|
||||
void FindBugs();
|
||||
|
||||
private:
|
||||
Core::TelemetrySession& telemetry_session;
|
||||
Vendor vendor = Vendor::Unknown;
|
||||
DriverBug bugs{};
|
||||
bool is_suitable{};
|
||||
|
||||
bool ext_buffer_storage{};
|
||||
bool arb_buffer_storage{};
|
||||
bool arb_clear_texture{};
|
||||
bool arb_get_texture_sub_image{};
|
||||
bool ext_clip_cull_distance{};
|
||||
|
||||
std::string_view gl_version{};
|
||||
std::string_view gpu_vendor{};
|
||||
std::string_view gpu_model{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
File diff suppressed because it is too large
Load diff
|
@ -3,12 +3,11 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
#include "common/vector_math.h"
|
||||
|
||||
#include "core/hw/gpu.h"
|
||||
#include "video_core/pica_types.h"
|
||||
#include "video_core/rasterizer_accelerated.h"
|
||||
#include "video_core/rasterizer_cache/rasterizer_cache.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/regs_lighting.h"
|
||||
#include "video_core/regs_texturing.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
@ -20,20 +19,20 @@ class EmuWindow;
|
|||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Driver;
|
||||
class ShaderProgramManager;
|
||||
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
|
||||
public:
|
||||
explicit RasterizerOpenGL(Frontend::EmuWindow& emu_window);
|
||||
explicit RasterizerOpenGL(Memory::MemorySystem& memory, Frontend::EmuWindow& emu_window,
|
||||
Driver& driver);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) override;
|
||||
|
||||
void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1,
|
||||
const Pica::Shader::OutputVertex& v2) override;
|
||||
void DrawTriangles() override;
|
||||
void NotifyPicaRegisterChanged(u32 id) override;
|
||||
void FlushAll() override;
|
||||
void FlushRegion(PAddr addr, u32 size) override;
|
||||
void InvalidateRegion(PAddr addr, u32 size) override;
|
||||
|
@ -46,10 +45,10 @@ public:
|
|||
u32 pixel_stride, ScreenInfo& screen_info) override;
|
||||
bool AccelerateDrawBatch(bool is_indexed) override;
|
||||
|
||||
/// Syncs entire status to match PICA registers
|
||||
void SyncEntireState() override;
|
||||
|
||||
private:
|
||||
void SyncFixedState() override;
|
||||
void NotifyFixedFunctionPicaRegisterChanged(u32 id) override;
|
||||
|
||||
struct SamplerInfo {
|
||||
using TextureConfig = Pica::TexturingRegs::TextureConfig;
|
||||
|
||||
|
@ -76,66 +75,15 @@ private:
|
|||
bool supress_mipmap_for_cube = false;
|
||||
};
|
||||
|
||||
/// Structure that the hardware rendered vertices are composed of
|
||||
struct HardwareVertex {
|
||||
HardwareVertex() = default;
|
||||
HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) {
|
||||
position[0] = v.pos.x.ToFloat32();
|
||||
position[1] = v.pos.y.ToFloat32();
|
||||
position[2] = v.pos.z.ToFloat32();
|
||||
position[3] = v.pos.w.ToFloat32();
|
||||
color[0] = v.color.x.ToFloat32();
|
||||
color[1] = v.color.y.ToFloat32();
|
||||
color[2] = v.color.z.ToFloat32();
|
||||
color[3] = v.color.w.ToFloat32();
|
||||
tex_coord0[0] = v.tc0.x.ToFloat32();
|
||||
tex_coord0[1] = v.tc0.y.ToFloat32();
|
||||
tex_coord1[0] = v.tc1.x.ToFloat32();
|
||||
tex_coord1[1] = v.tc1.y.ToFloat32();
|
||||
tex_coord2[0] = v.tc2.x.ToFloat32();
|
||||
tex_coord2[1] = v.tc2.y.ToFloat32();
|
||||
tex_coord0_w = v.tc0_w.ToFloat32();
|
||||
normquat[0] = v.quat.x.ToFloat32();
|
||||
normquat[1] = v.quat.y.ToFloat32();
|
||||
normquat[2] = v.quat.z.ToFloat32();
|
||||
normquat[3] = v.quat.w.ToFloat32();
|
||||
view[0] = v.view.x.ToFloat32();
|
||||
view[1] = v.view.y.ToFloat32();
|
||||
view[2] = v.view.z.ToFloat32();
|
||||
|
||||
if (flip_quaternion) {
|
||||
normquat = -normquat;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Vec4f position;
|
||||
Common::Vec4f color;
|
||||
Common::Vec2f tex_coord0;
|
||||
Common::Vec2f tex_coord1;
|
||||
Common::Vec2f tex_coord2;
|
||||
float tex_coord0_w;
|
||||
Common::Vec4f normquat;
|
||||
Common::Vec3f view;
|
||||
};
|
||||
|
||||
/// Syncs the clip enabled status to match the PICA register
|
||||
void SyncClipEnabled();
|
||||
|
||||
/// Syncs the clip coefficients to match the PICA register
|
||||
void SyncClipCoef();
|
||||
|
||||
/// Sets the OpenGL shader in accordance with the current PICA register state
|
||||
void SetShader();
|
||||
|
||||
/// Syncs the cull mode to match the PICA register
|
||||
void SyncCullMode();
|
||||
|
||||
/// Syncs the depth scale to match the PICA register
|
||||
void SyncDepthScale();
|
||||
|
||||
/// Syncs the depth offset to match the PICA register
|
||||
void SyncDepthOffset();
|
||||
|
||||
/// Syncs the blend enabled status to match the PICA register
|
||||
void SyncBlendEnabled();
|
||||
|
||||
|
@ -145,18 +93,6 @@ private:
|
|||
/// Syncs the blend color to match the PICA register
|
||||
void SyncBlendColor();
|
||||
|
||||
/// Syncs the fog states to match the PICA register
|
||||
void SyncFogColor();
|
||||
|
||||
/// Sync the procedural texture noise configuration to match the PICA register
|
||||
void SyncProcTexNoise();
|
||||
|
||||
/// Sync the procedural texture bias configuration to match the PICA register
|
||||
void SyncProcTexBias();
|
||||
|
||||
/// Syncs the alpha test states to match the PICA register
|
||||
void SyncAlphaTest();
|
||||
|
||||
/// Syncs the logic op states to match the PICA register
|
||||
void SyncLogicOp();
|
||||
|
||||
|
@ -175,46 +111,6 @@ private:
|
|||
/// Syncs the depth test states to match the PICA register
|
||||
void SyncDepthTest();
|
||||
|
||||
/// Syncs the TEV combiner color buffer to match the PICA register
|
||||
void SyncCombinerColor();
|
||||
|
||||
/// Syncs the TEV constant color to match the PICA register
|
||||
void SyncTevConstColor(std::size_t tev_index,
|
||||
const Pica::TexturingRegs::TevStageConfig& tev_stage);
|
||||
|
||||
/// Syncs the lighting global ambient color to match the PICA register
|
||||
void SyncGlobalAmbient();
|
||||
|
||||
/// Syncs the specified light's specular 0 color to match the PICA register
|
||||
void SyncLightSpecular0(int light_index);
|
||||
|
||||
/// Syncs the specified light's specular 1 color to match the PICA register
|
||||
void SyncLightSpecular1(int light_index);
|
||||
|
||||
/// Syncs the specified light's diffuse color to match the PICA register
|
||||
void SyncLightDiffuse(int light_index);
|
||||
|
||||
/// Syncs the specified light's ambient color to match the PICA register
|
||||
void SyncLightAmbient(int light_index);
|
||||
|
||||
/// Syncs the specified light's position to match the PICA register
|
||||
void SyncLightPosition(int light_index);
|
||||
|
||||
/// Syncs the specified spot light direcition to match the PICA register
|
||||
void SyncLightSpotDirection(int light_index);
|
||||
|
||||
/// Syncs the specified light's distance attenuation bias to match the PICA register
|
||||
void SyncLightDistanceAttenuationBias(int light_index);
|
||||
|
||||
/// Syncs the specified light's distance attenuation scale to match the PICA register
|
||||
void SyncLightDistanceAttenuationScale(int light_index);
|
||||
|
||||
/// Syncs the shadow rendering bias to match the PICA register
|
||||
void SyncShadowBias();
|
||||
|
||||
/// Syncs the shadow texture bias to match the PICA register
|
||||
void SyncShadowTextureBias();
|
||||
|
||||
/// Syncs and uploads the lighting, fog and proctex LUTs
|
||||
void SyncAndUploadLUTs();
|
||||
void SyncAndUploadLUTsLF();
|
||||
|
@ -228,15 +124,6 @@ private:
|
|||
/// Internal implementation for AccelerateDrawBatch
|
||||
bool AccelerateDrawBatchInternal(bool is_indexed);
|
||||
|
||||
struct VertexArrayInfo {
|
||||
u32 vs_input_index_min;
|
||||
u32 vs_input_index_max;
|
||||
u32 vs_input_size;
|
||||
};
|
||||
|
||||
/// Retrieve the range and the size of the input vertex
|
||||
VertexArrayInfo AnalyzeVertexArray(bool is_indexed);
|
||||
|
||||
/// Setup vertex array for AccelerateDrawBatch
|
||||
void SetupVertexArray(u8* array_ptr, GLintptr buffer_offset, GLuint vs_input_index_min,
|
||||
GLuint vs_input_index_max);
|
||||
|
@ -247,38 +134,13 @@ private:
|
|||
/// Setup geometry shader for AccelerateDrawBatch
|
||||
bool SetupGeometryShader();
|
||||
|
||||
bool is_amd;
|
||||
|
||||
private:
|
||||
Driver& driver;
|
||||
OpenGLState state;
|
||||
GLuint default_texture;
|
||||
|
||||
RasterizerCacheOpenGL res_cache;
|
||||
|
||||
std::vector<HardwareVertex> vertex_batch;
|
||||
|
||||
bool shader_dirty = true;
|
||||
|
||||
struct {
|
||||
UniformData data;
|
||||
std::array<bool, Pica::LightingRegs::NumLightingSampler> lighting_lut_dirty;
|
||||
bool lighting_lut_dirty_any;
|
||||
bool fog_lut_dirty;
|
||||
bool proctex_noise_lut_dirty;
|
||||
bool proctex_color_map_dirty;
|
||||
bool proctex_alpha_map_dirty;
|
||||
bool proctex_lut_dirty;
|
||||
bool proctex_diff_lut_dirty;
|
||||
bool dirty;
|
||||
} uniform_block_data = {};
|
||||
|
||||
std::unique_ptr<ShaderProgramManager> shader_program_manager;
|
||||
|
||||
// They shall be big enough for about one frame.
|
||||
static constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
static constexpr std::size_t INDEX_BUFFER_SIZE = 1 * 1024 * 1024;
|
||||
static constexpr std::size_t UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||
static constexpr std::size_t TEXTURE_BUFFER_SIZE = 1 * 1024 * 1024;
|
||||
|
||||
OGLVertexArray sw_vao; // VAO for software shader draw
|
||||
OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
|
||||
std::array<bool, 16> hw_vao_enabled_attributes{};
|
||||
|
@ -299,15 +161,6 @@ private:
|
|||
OGLTexture texture_buffer_lut_lf;
|
||||
OGLTexture texture_buffer_lut_rg;
|
||||
OGLTexture texture_buffer_lut_rgba;
|
||||
|
||||
std::array<std::array<Common::Vec2f, 256>, Pica::LightingRegs::NumLightingSampler>
|
||||
lighting_lut_data{};
|
||||
std::array<Common::Vec2f, 128> fog_lut_data{};
|
||||
std::array<Common::Vec2f, 128> proctex_noise_lut_data{};
|
||||
std::array<Common::Vec2f, 128> proctex_color_map_data{};
|
||||
std::array<Common::Vec2f, 128> proctex_alpha_map_data{};
|
||||
std::array<Common::Vec4f, 256> proctex_lut_data{};
|
||||
std::array<Common::Vec4f, 256> proctex_diff_lut_data{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||
#include "video_core/renderer_opengl/gl_vars.h"
|
||||
#include "video_core/shader/shader_uniforms.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
using Pica::FramebufferRegs;
|
||||
|
@ -23,53 +24,7 @@ using VSOutputAttributes = RasterizerRegs::VSOutputAttributes;
|
|||
|
||||
namespace OpenGL {
|
||||
|
||||
constexpr std::string_view UniformBlockDef = R"(
|
||||
#define NUM_TEV_STAGES 6
|
||||
#define NUM_LIGHTS 8
|
||||
#define NUM_LIGHTING_SAMPLERS 24
|
||||
|
||||
struct LightSrc {
|
||||
vec3 specular_0;
|
||||
vec3 specular_1;
|
||||
vec3 diffuse;
|
||||
vec3 ambient;
|
||||
vec3 position;
|
||||
vec3 spot_direction;
|
||||
float dist_atten_bias;
|
||||
float dist_atten_scale;
|
||||
};
|
||||
|
||||
layout (std140) uniform shader_data {
|
||||
int framebuffer_scale;
|
||||
int alphatest_ref;
|
||||
float depth_scale;
|
||||
float depth_offset;
|
||||
float shadow_bias_constant;
|
||||
float shadow_bias_linear;
|
||||
int scissor_x1;
|
||||
int scissor_y1;
|
||||
int scissor_x2;
|
||||
int scissor_y2;
|
||||
int fog_lut_offset;
|
||||
int proctex_noise_lut_offset;
|
||||
int proctex_color_map_offset;
|
||||
int proctex_alpha_map_offset;
|
||||
int proctex_lut_offset;
|
||||
int proctex_diff_lut_offset;
|
||||
float proctex_bias;
|
||||
int shadow_texture_bias;
|
||||
ivec4 lighting_lut_offset[NUM_LIGHTING_SAMPLERS / 4];
|
||||
vec3 fog_color;
|
||||
vec2 proctex_noise_f;
|
||||
vec2 proctex_noise_a;
|
||||
vec2 proctex_noise_p;
|
||||
vec3 lighting_global_ambient;
|
||||
LightSrc light_src[NUM_LIGHTS];
|
||||
vec4 const_color[NUM_TEV_STAGES];
|
||||
vec4 tev_combiner_buffer_color;
|
||||
vec4 clip_coef;
|
||||
};
|
||||
)";
|
||||
const std::string UniformBlockDef = Pica::Shader::BuildShaderUniformDefinitions();
|
||||
|
||||
static std::string GetVertexInterfaceDeclaration(bool is_output, bool separable_shader) {
|
||||
std::string out;
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
#include <set>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <boost/variant.hpp>
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include <variant>
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_vars.h"
|
||||
#include "video_core/shader/shader_uniforms.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
@ -85,7 +85,8 @@ static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw(
|
|||
return {PicaVSConfig{raw.GetRawShaderConfig().vs, setup}, setup};
|
||||
}
|
||||
|
||||
static void SetShaderUniformBlockBinding(GLuint shader, const char* name, UniformBindings binding,
|
||||
static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
|
||||
Pica::Shader::UniformBindings binding,
|
||||
std::size_t expected_size) {
|
||||
const GLuint ub_index = glGetUniformBlockIndex(shader, name);
|
||||
if (ub_index == GL_INVALID_INDEX) {
|
||||
|
@ -100,9 +101,10 @@ static void SetShaderUniformBlockBinding(GLuint shader, const char* name, Unifor
|
|||
}
|
||||
|
||||
static void SetShaderUniformBlockBindings(GLuint shader) {
|
||||
SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common,
|
||||
sizeof(UniformData));
|
||||
SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS, sizeof(VSUniformData));
|
||||
SetShaderUniformBlockBinding(shader, "shader_data", Pica::Shader::UniformBindings::Common,
|
||||
sizeof(Pica::Shader::UniformData));
|
||||
SetShaderUniformBlockBinding(shader, "vs_config", Pica::Shader::UniformBindings::VS,
|
||||
sizeof(Pica::Shader::VSUniformData));
|
||||
}
|
||||
|
||||
static void SetShaderSamplerBinding(GLuint shader, const char* name,
|
||||
|
@ -148,21 +150,6 @@ static void SetShaderSamplerBindings(GLuint shader) {
|
|||
cur_state.Apply();
|
||||
}
|
||||
|
||||
void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs,
|
||||
const Pica::Shader::ShaderSetup& setup) {
|
||||
std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools),
|
||||
[](bool value) -> BoolAligned { return {value ? GL_TRUE : GL_FALSE}; });
|
||||
std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i),
|
||||
[](const auto& value) -> Common::Vec4u {
|
||||
return {value.x.Value(), value.y.Value(), value.z.Value(), value.w.Value()};
|
||||
});
|
||||
std::transform(std::begin(setup.uniforms.f), std::end(setup.uniforms.f), std::begin(f),
|
||||
[](const auto& value) -> Common::Vec4f {
|
||||
return {value.x.ToFloat32(), value.y.ToFloat32(), value.z.ToFloat32(),
|
||||
value.w.ToFloat32()};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* An object representing a shader program staging. It can be either a shader object or a program
|
||||
* object, depending on whether separable program is used.
|
||||
|
@ -178,12 +165,12 @@ public:
|
|||
}
|
||||
|
||||
void Create(const char* source, GLenum type) {
|
||||
if (shader_or_program.which() == 0) {
|
||||
boost::get<OGLShader>(shader_or_program).Create(source, type);
|
||||
if (shader_or_program.index() == 0) {
|
||||
std::get<OGLShader>(shader_or_program).Create(source, type);
|
||||
} else {
|
||||
OGLShader shader;
|
||||
shader.Create(source, type);
|
||||
OGLProgram& program = boost::get<OGLProgram>(shader_or_program);
|
||||
OGLProgram& program = std::get<OGLProgram>(shader_or_program);
|
||||
program.Create(true, {shader.handle});
|
||||
SetShaderUniformBlockBindings(program.handle);
|
||||
|
||||
|
@ -194,10 +181,10 @@ public:
|
|||
}
|
||||
|
||||
GLuint GetHandle() const {
|
||||
if (shader_or_program.which() == 0) {
|
||||
return boost::get<OGLShader>(shader_or_program).handle;
|
||||
if (shader_or_program.index() == 0) {
|
||||
return std::get<OGLShader>(shader_or_program).handle;
|
||||
} else {
|
||||
return boost::get<OGLProgram>(shader_or_program).handle;
|
||||
return std::get<OGLProgram>(shader_or_program).handle;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +195,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
boost::variant<OGLShader, OGLProgram> shader_or_program;
|
||||
std::variant<OGLShader, OGLProgram> shader_or_program;
|
||||
};
|
||||
|
||||
class TrivialVertexShader {
|
||||
|
@ -329,8 +316,8 @@ using FragmentShaders = ShaderCache<PicaFSConfig, &GenerateFragmentShader, GL_FR
|
|||
|
||||
class ShaderProgramManager::Impl {
|
||||
public:
|
||||
explicit Impl(bool separable, bool is_amd)
|
||||
: is_amd(is_amd), separable(separable), programmable_vertex_shaders(separable),
|
||||
explicit Impl(bool separable)
|
||||
: separable(separable), programmable_vertex_shaders(separable),
|
||||
trivial_vertex_shader(separable), fixed_geometry_shaders(separable),
|
||||
fragment_shaders(separable), disk_cache(separable) {
|
||||
if (separable)
|
||||
|
@ -363,7 +350,6 @@ public:
|
|||
static_assert(offsetof(ShaderTuple, fs_hash) == sizeof(std::size_t) * 2,
|
||||
"ShaderTuple layout changed!");
|
||||
|
||||
bool is_amd;
|
||||
bool separable;
|
||||
|
||||
ShaderTuple current;
|
||||
|
@ -379,9 +365,9 @@ public:
|
|||
ShaderDiskCache disk_cache;
|
||||
};
|
||||
|
||||
ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, bool separable,
|
||||
bool is_amd)
|
||||
: impl(std::make_unique<Impl>(separable, is_amd)), emu_window{emu_window_} {}
|
||||
ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, const Driver& driver_,
|
||||
bool separable)
|
||||
: impl(std::make_unique<Impl>(separable)), emu_window{emu_window_}, driver{driver_} {}
|
||||
|
||||
ShaderProgramManager::~ShaderProgramManager() = default;
|
||||
|
||||
|
@ -443,10 +429,7 @@ void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs) {
|
|||
|
||||
void ShaderProgramManager::ApplyTo(OpenGLState& state) {
|
||||
if (impl->separable) {
|
||||
if (impl->is_amd) {
|
||||
// Without this reseting, AMD sometimes freezes when one stage is changed but not
|
||||
// for the others. On the other hand, including this reset seems to introduce memory
|
||||
// leak in Intel Graphics.
|
||||
if (driver.HasBug(DriverBug::ShaderStageChangeFreeze)) {
|
||||
glUseProgramStages(
|
||||
impl->pipeline.handle,
|
||||
GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 0);
|
||||
|
@ -641,7 +624,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
|
|||
std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
|
||||
const auto LoadRawSepareble = [&](Frontend::GraphicsContext* context, std::size_t begin,
|
||||
std::size_t end) {
|
||||
Frontend::ScopeAcquireContext scope(*context);
|
||||
const auto scope = context->Acquire();
|
||||
for (std::size_t i = begin; i < end; ++i) {
|
||||
if (stop_loading || compilation_failed) {
|
||||
return;
|
||||
|
|
|
@ -5,13 +5,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/regs_lighting.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Frontend {
|
||||
class EmuWindow;
|
||||
|
@ -19,8 +13,7 @@ class EmuWindow;
|
|||
|
||||
namespace Pica {
|
||||
struct Regs;
|
||||
struct ShaderRegs;
|
||||
} // namespace Pica
|
||||
}
|
||||
|
||||
namespace Pica::Shader {
|
||||
struct ShaderSetup;
|
||||
|
@ -28,87 +21,13 @@ struct ShaderSetup;
|
|||
|
||||
namespace OpenGL {
|
||||
|
||||
enum class UniformBindings : u32 { Common, VS, GS };
|
||||
|
||||
struct LightSrc {
|
||||
alignas(16) Common::Vec3f specular_0;
|
||||
alignas(16) Common::Vec3f specular_1;
|
||||
alignas(16) Common::Vec3f diffuse;
|
||||
alignas(16) Common::Vec3f ambient;
|
||||
alignas(16) Common::Vec3f position;
|
||||
alignas(16) Common::Vec3f spot_direction; // negated
|
||||
float dist_atten_bias;
|
||||
float dist_atten_scale;
|
||||
};
|
||||
|
||||
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
|
||||
// NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at
|
||||
// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
||||
// Not following that rule will cause problems on some AMD drivers.
|
||||
struct UniformData {
|
||||
int framebuffer_scale;
|
||||
int alphatest_ref;
|
||||
float depth_scale;
|
||||
float depth_offset;
|
||||
float shadow_bias_constant;
|
||||
float shadow_bias_linear;
|
||||
int scissor_x1;
|
||||
int scissor_y1;
|
||||
int scissor_x2;
|
||||
int scissor_y2;
|
||||
int fog_lut_offset;
|
||||
int proctex_noise_lut_offset;
|
||||
int proctex_color_map_offset;
|
||||
int proctex_alpha_map_offset;
|
||||
int proctex_lut_offset;
|
||||
int proctex_diff_lut_offset;
|
||||
float proctex_bias;
|
||||
int shadow_texture_bias;
|
||||
alignas(16) Common::Vec4i lighting_lut_offset[Pica::LightingRegs::NumLightingSampler / 4];
|
||||
alignas(16) Common::Vec3f fog_color;
|
||||
alignas(8) Common::Vec2f proctex_noise_f;
|
||||
alignas(8) Common::Vec2f proctex_noise_a;
|
||||
alignas(8) Common::Vec2f proctex_noise_p;
|
||||
alignas(16) Common::Vec3f lighting_global_ambient;
|
||||
LightSrc light_src[8];
|
||||
alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
|
||||
alignas(16) Common::Vec4f tev_combiner_buffer_color;
|
||||
alignas(16) Common::Vec4f clip_coef;
|
||||
};
|
||||
|
||||
static_assert(sizeof(UniformData) == 0x4F0,
|
||||
"The size of the UniformData does not match the structure in the shader");
|
||||
static_assert(sizeof(UniformData) < 16384,
|
||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
/// Uniform struct for the Uniform Buffer Object that contains PICA vertex/geometry shader uniforms.
|
||||
// NOTE: the same rule from UniformData also applies here.
|
||||
struct PicaUniformsData {
|
||||
void SetFromRegs(const Pica::ShaderRegs& regs, const Pica::Shader::ShaderSetup& setup);
|
||||
|
||||
struct BoolAligned {
|
||||
alignas(16) int b;
|
||||
};
|
||||
|
||||
std::array<BoolAligned, 16> bools;
|
||||
alignas(16) std::array<Common::Vec4u, 4> i;
|
||||
alignas(16) std::array<Common::Vec4f, 96> f;
|
||||
};
|
||||
|
||||
struct VSUniformData {
|
||||
PicaUniformsData uniforms;
|
||||
};
|
||||
static_assert(sizeof(VSUniformData) == 1856,
|
||||
"The size of the VSUniformData does not match the structure in the shader");
|
||||
static_assert(sizeof(VSUniformData) < 16384,
|
||||
"VSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
class Driver;
|
||||
class OpenGLState;
|
||||
|
||||
/// A class that manage different shader stages and configures them with given config data.
|
||||
class ShaderProgramManager {
|
||||
public:
|
||||
ShaderProgramManager(Frontend::EmuWindow& emu_window_, bool separable, bool is_amd);
|
||||
ShaderProgramManager(Frontend::EmuWindow& emu_window, const Driver& driver, bool separable);
|
||||
~ShaderProgramManager();
|
||||
|
||||
void LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||
|
@ -131,5 +50,6 @@ private:
|
|||
std::unique_ptr<Impl> impl;
|
||||
|
||||
Frontend::EmuWindow& emu_window;
|
||||
const Driver& driver;
|
||||
};
|
||||
} // namespace OpenGL
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
|
||||
|
@ -12,19 +13,14 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
|
|||
|
||||
namespace OpenGL {
|
||||
|
||||
OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool array_buffer_for_amd,
|
||||
OGLStreamBuffer::OGLStreamBuffer(Driver& driver, GLenum target, GLsizeiptr size,
|
||||
bool prefer_coherent)
|
||||
: gl_target(target), buffer_size(size) {
|
||||
gl_buffer.Create();
|
||||
glBindBuffer(gl_target, gl_buffer.handle);
|
||||
|
||||
GLsizeiptr allocate_size = size;
|
||||
if (array_buffer_for_amd) {
|
||||
// On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer
|
||||
// read position is near the end and is an out-of-bound access to the vertex buffer. This is
|
||||
// probably a bug in the driver and is related to the usage of vec3<byte> attributes in the
|
||||
// vertex array. Doubling the allocation size for the vertex buffer seems to avoid the
|
||||
// crash.
|
||||
if (driver.HasBug(DriverBug::VertexArrayOutOfBound) && target == GL_ARRAY_BUFFER) {
|
||||
allocate_size *= 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,17 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Driver;
|
||||
|
||||
class OGLStreamBuffer : private NonCopyable {
|
||||
public:
|
||||
explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool array_buffer_for_amd,
|
||||
explicit OGLStreamBuffer(Driver& driver, GLenum target, GLsizeiptr size,
|
||||
bool prefer_coherent = false);
|
||||
~OGLStreamBuffer();
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include "core/hw/hw.h"
|
||||
#include "core/hw/lcd.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/tracer/recorder.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
@ -352,14 +350,17 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
|
|||
return matrix;
|
||||
}
|
||||
|
||||
RendererOpenGL::RendererOpenGL(Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window)
|
||||
: RendererBase{window, secondary_window},
|
||||
frame_dumper(Core::System::GetInstance().VideoDumper(), window) {
|
||||
RendererOpenGL::RendererOpenGL(Core::System& system, Frontend::EmuWindow& window,
|
||||
Frontend::EmuWindow* secondary_window)
|
||||
: VideoCore::RendererBase{system, window, secondary_window}, driver{system.TelemetrySession()},
|
||||
frame_dumper{system.VideoDumper(), window} {
|
||||
window.mailbox = std::make_unique<OGLTextureMailbox>();
|
||||
if (secondary_window) {
|
||||
secondary_window->mailbox = std::make_unique<OGLTextureMailbox>();
|
||||
}
|
||||
frame_dumper.mailbox = std::make_unique<OGLVideoDumpingMailbox>();
|
||||
InitOpenGLObjects();
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system.Memory(), render_window, driver);
|
||||
}
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
@ -374,7 +375,6 @@ void RendererOpenGL::SwapBuffers() {
|
|||
state.Apply();
|
||||
|
||||
PrepareRendertarget();
|
||||
|
||||
RenderScreenshot();
|
||||
|
||||
const auto& main_layout = render_window.GetFramebufferLayout();
|
||||
|
@ -396,26 +396,12 @@ void RendererOpenGL::SwapBuffers() {
|
|||
}
|
||||
}
|
||||
|
||||
m_current_frame++;
|
||||
|
||||
Core::System::GetInstance().perf_stats->EndSystemFrame();
|
||||
|
||||
render_window.PollEvents();
|
||||
|
||||
Core::System::GetInstance().frame_limiter.DoFrameLimiting(
|
||||
Core::System::GetInstance().CoreTiming().GetGlobalTimeUs());
|
||||
Core::System::GetInstance().perf_stats->BeginSystemFrame();
|
||||
|
||||
EndFrame();
|
||||
prev_state.Apply();
|
||||
RefreshRasterizerSetting();
|
||||
|
||||
if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
|
||||
Pica::g_debug_context->recorder->FrameFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void RendererOpenGL::RenderScreenshot() {
|
||||
if (VideoCore::g_renderer_screenshot_requested) {
|
||||
if (renderer_settings.screenshot_requested.exchange(false)) {
|
||||
// Draw this frame to the screenshot framebuffer
|
||||
screenshot_framebuffer.Create();
|
||||
GLuint old_read_fb = state.draw.read_framebuffer;
|
||||
|
@ -423,7 +409,7 @@ void RendererOpenGL::RenderScreenshot() {
|
|||
state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
|
||||
state.Apply();
|
||||
|
||||
Layout::FramebufferLayout layout{VideoCore::g_screenshot_framebuffer_layout};
|
||||
const auto layout{renderer_settings.screenshot_framebuffer_layout};
|
||||
|
||||
GLuint renderbuffer;
|
||||
glGenRenderbuffers(1, &renderbuffer);
|
||||
|
@ -435,7 +421,7 @@ void RendererOpenGL::RenderScreenshot() {
|
|||
DrawScreens(layout, false);
|
||||
|
||||
glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
VideoCore::g_screenshot_bits);
|
||||
renderer_settings.screenshot_bits);
|
||||
|
||||
screenshot_framebuffer.Release();
|
||||
state.draw.read_framebuffer = old_read_fb;
|
||||
|
@ -443,8 +429,7 @@ void RendererOpenGL::RenderScreenshot() {
|
|||
state.Apply();
|
||||
glDeleteRenderbuffers(1, &renderbuffer);
|
||||
|
||||
VideoCore::g_screenshot_complete_callback();
|
||||
VideoCore::g_renderer_screenshot_requested = false;
|
||||
renderer_settings.screenshot_complete_callback();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1226,109 +1211,8 @@ void RendererOpenGL::CleanupVideoDumping() {
|
|||
mailbox->free_cv.notify_one();
|
||||
}
|
||||
|
||||
static const char* GetSource(GLenum source) {
|
||||
#define RET(s) \
|
||||
case GL_DEBUG_SOURCE_##s: \
|
||||
return #s
|
||||
switch (source) {
|
||||
RET(API);
|
||||
RET(WINDOW_SYSTEM);
|
||||
RET(SHADER_COMPILER);
|
||||
RET(THIRD_PARTY);
|
||||
RET(APPLICATION);
|
||||
RET(OTHER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
#undef RET
|
||||
|
||||
return "";
|
||||
void RendererOpenGL::Sync() {
|
||||
rasterizer->SyncEntireState();
|
||||
}
|
||||
|
||||
static const char* GetType(GLenum type) {
|
||||
#define RET(t) \
|
||||
case GL_DEBUG_TYPE_##t: \
|
||||
return #t
|
||||
switch (type) {
|
||||
RET(ERROR);
|
||||
RET(DEPRECATED_BEHAVIOR);
|
||||
RET(UNDEFINED_BEHAVIOR);
|
||||
RET(PORTABILITY);
|
||||
RET(PERFORMANCE);
|
||||
RET(OTHER);
|
||||
RET(MARKER);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
#undef RET
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar* message, const void* user_param) {
|
||||
Log::Level level;
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
level = Log::Level::Critical;
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
level = Log::Level::Warning;
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
level = Log::Level::Debug;
|
||||
break;
|
||||
}
|
||||
LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type),
|
||||
id, message);
|
||||
}
|
||||
|
||||
/// Initialize the renderer
|
||||
VideoCore::ResultStatus RendererOpenGL::Init() {
|
||||
#ifndef ANDROID
|
||||
if (!gladLoadGL()) {
|
||||
return VideoCore::ResultStatus::ErrorBelowGL43;
|
||||
}
|
||||
|
||||
// Qualcomm has some spammy info messages that are marked as errors but not important
|
||||
// https://developer.qualcomm.com/comment/11845
|
||||
if (GLAD_GL_KHR_debug) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageCallback(DebugHandler, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
const std::string_view gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
|
||||
const std::string_view gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
|
||||
const std::string_view gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))};
|
||||
|
||||
LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
|
||||
LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
|
||||
LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
|
||||
|
||||
auto& telemetry_session = Core::System::GetInstance().TelemetrySession();
|
||||
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
|
||||
telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor));
|
||||
telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model));
|
||||
telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
|
||||
|
||||
if (gpu_vendor == "GDI Generic") {
|
||||
return VideoCore::ResultStatus::ErrorGenericDrivers;
|
||||
}
|
||||
|
||||
if (!(GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1)) {
|
||||
return VideoCore::ResultStatus::ErrorBelowGL43;
|
||||
}
|
||||
|
||||
InitOpenGLObjects();
|
||||
|
||||
RefreshRasterizerSetting();
|
||||
|
||||
return VideoCore::ResultStatus::Success;
|
||||
}
|
||||
|
||||
/// Shutdown the renderer
|
||||
void RendererOpenGL::ShutDown() {}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "core/hw/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/frame_dumper_opengl.h"
|
||||
#include "video_core/renderer_opengl/gl_driver.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
|
@ -15,6 +17,10 @@ namespace Layout {
|
|||
struct FramebufferLayout;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
struct Frame {
|
||||
|
@ -48,35 +54,21 @@ struct ScreenInfo {
|
|||
TextureInfo texture;
|
||||
};
|
||||
|
||||
struct PresentationTexture {
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
OGLTexture texture;
|
||||
};
|
||||
|
||||
class RendererOpenGL : public RendererBase {
|
||||
class RendererOpenGL : public VideoCore::RendererBase {
|
||||
public:
|
||||
explicit RendererOpenGL(Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window);
|
||||
explicit RendererOpenGL(Core::System& system, Frontend::EmuWindow& window,
|
||||
Frontend::EmuWindow* secondary_window);
|
||||
~RendererOpenGL() override;
|
||||
|
||||
/// Initialize the renderer
|
||||
VideoCore::ResultStatus Init() override;
|
||||
[[nodiscard]] VideoCore::RasterizerInterface* Rasterizer() const override {
|
||||
return rasterizer.get();
|
||||
}
|
||||
|
||||
/// Shutdown the renderer
|
||||
void ShutDown() override;
|
||||
|
||||
/// Finalizes rendering the guest frame
|
||||
void SwapBuffers() override;
|
||||
|
||||
/// Draws the latest frame from texture mailbox to the currently bound draw framebuffer in this
|
||||
/// context
|
||||
void TryPresent(int timeout_ms, bool is_secondary) override;
|
||||
|
||||
/// Prepares for video dumping (e.g. create necessary buffers, etc)
|
||||
void PrepareVideoDumping() override;
|
||||
|
||||
/// Cleans up after video dumping is ended
|
||||
void CleanupVideoDumping() override;
|
||||
void Sync() override;
|
||||
|
||||
private:
|
||||
void InitOpenGLObjects();
|
||||
|
@ -111,7 +103,10 @@ private:
|
|||
// Fills active OpenGL texture with the given RGB color.
|
||||
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture);
|
||||
|
||||
private:
|
||||
Driver driver;
|
||||
OpenGLState state;
|
||||
std::unique_ptr<RasterizerOpenGL> rasterizer;
|
||||
|
||||
// OpenGL object IDs
|
||||
OGLVertexArray vertex_array;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue