@@ -28,9 +28,7 @@ void FlushPipeline()

void SetGenerationMode()
{
RasterizationState state = {};
state.Generate(bpmem, g_vertex_manager->GetCurrentPrimitiveType());
g_renderer->SetRasterizationState(state);
g_vertex_manager->SetRasterizationStateChanged();
}

void SetScissor()
@@ -129,16 +127,12 @@ void SetViewport()

void SetDepthMode()
{
DepthState state = {};
state.Generate(bpmem);
g_renderer->SetDepthState(state);
g_vertex_manager->SetDepthStateChanged();
}

void SetBlendMode()
{
BlendingState state = {};
state.Generate(bpmem);
g_renderer->SetBlendingState(state);
g_vertex_manager->SetBlendingStateChanged();
}

/* Explanation of the magic behind ClearScreen:
@@ -32,6 +32,7 @@ set(SRCS
PostProcessing.cpp
RenderBase.cpp
RenderState.cpp
ShaderCache.cpp
ShaderGenCommon.cpp
Statistics.cpp
UberShaderCommon.cpp
@@ -102,8 +102,6 @@ static BugInfo m_known_bugs[] = {
true},
{API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_I965, Family::UNKNOWN,
BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true},
{API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_NOUVEAU, Family::UNKNOWN,
BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true},
{API_VULKAN, OS_ALL, VENDOR_NVIDIA, DRIVER_NVIDIA, Family::UNKNOWN, BUG_BROKEN_MSAA_CLEAR, -1.0,
-1.0, true},
{API_VULKAN, OS_ALL, VENDOR_IMGTEC, DRIVER_IMGTEC, Family::UNKNOWN,
@@ -252,8 +252,10 @@ enum Bug
// the negated value to a temporary variable then using that in the bitwise op.
BUG_BROKEN_BITWISE_OP_NEGATION,

// Bug: Shaders are recompiled on the main thread after being previously compiled on
// a worker thread on Mesa i965.
// BUG: The GPU shader code appears to be context-specific on Mesa/i965.
// This means that if we compiled the ubershaders asynchronously, they will be recompiled
// on the main thread the first time they are used, causing stutter. For now, disable
// asynchronous compilation on Mesa i965.
// Started version: -1
// Ended Version: -1
BUG_SHARED_CONTEXT_SHADER_COMPILATION,
@@ -0,0 +1,75 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/UberShaderPixel.h"
#include "VideoCommon/UberShaderVertex.h"
#include "VideoCommon/VertexShaderGen.h"

class NativeVertexFormat;

namespace VideoCommon
{
struct GXPipelineUid
{
const NativeVertexFormat* vertex_format;
VertexShaderUid vs_uid;
GeometryShaderUid gs_uid;
PixelShaderUid ps_uid;
RasterizationState rasterization_state;
DepthState depth_state;
BlendingState blending_state;

// We use memcmp() for comparing pipelines as std::tie generates a large number of instructions,
// and this map lookup can happen every draw call. However, as using memcmp() will also compare
// any padding bytes, we have to ensure these are zeroed out.
GXPipelineUid() { std::memset(this, 0, sizeof(*this)); }
GXPipelineUid(const GXPipelineUid& rhs) { std::memcpy(this, &rhs, sizeof(*this)); }
GXPipelineUid& operator=(const GXPipelineUid& rhs)
{
std::memcpy(this, &rhs, sizeof(*this));
return *this;
}
bool operator<(const GXPipelineUid& rhs) const
{
return std::memcmp(this, &rhs, sizeof(*this)) < 0;
}
bool operator==(const GXPipelineUid& rhs) const
{
return std::memcmp(this, &rhs, sizeof(*this)) == 0;
}
bool operator!=(const GXPipelineUid& rhs) const { return !operator==(rhs); }
};
struct GXUberPipelineUid
{
const NativeVertexFormat* vertex_format;
UberShader::VertexShaderUid vs_uid;
GeometryShaderUid gs_uid;
UberShader::PixelShaderUid ps_uid;
RasterizationState rasterization_state;
DepthState depth_state;
BlendingState blending_state;

GXUberPipelineUid() { std::memset(this, 0, sizeof(*this)); }
GXUberPipelineUid(const GXUberPipelineUid& rhs) { std::memcpy(this, &rhs, sizeof(*this)); }
GXUberPipelineUid& operator=(const GXUberPipelineUid& rhs)
{
std::memcpy(this, &rhs, sizeof(*this));
return *this;
}
bool operator<(const GXUberPipelineUid& rhs) const
{
return std::memcmp(this, &rhs, sizeof(*this)) < 0;
}
bool operator==(const GXUberPipelineUid& rhs) const
{
return std::memcmp(this, &rhs, sizeof(*this)) == 0;
}
bool operator!=(const GXUberPipelineUid& rhs) const { return !operator==(rhs); }
};
} // namespace VideoCommon
@@ -8,7 +8,6 @@
#include "Common/CommonTypes.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/VertexManagerBase.h"

enum class APIType;

@@ -56,6 +56,7 @@
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/PostProcessing.h"
#include "VideoCommon/ShaderCache.h"
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/Statistics.h"
#include "VideoCommon/TextureCacheBase.h"
@@ -92,6 +93,7 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height)

m_surface_handle = Host_GetRenderHandle();
m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits;
m_last_efb_multisamples = g_ActiveConfig.iMultisamples;
}

Renderer::~Renderer() = default;
@@ -234,11 +236,20 @@ void Renderer::SaveScreenshot(const std::string& filename, bool wait_for_complet
bool Renderer::CheckForHostConfigChanges()
{
ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent();
if (new_host_config.bits == m_last_host_config_bits)
if (new_host_config.bits == m_last_host_config_bits &&
m_last_efb_multisamples == g_ActiveConfig.iMultisamples)
{
return false;
}

OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
m_last_host_config_bits = new_host_config.bits;
m_last_efb_multisamples = g_ActiveConfig.iMultisamples;

// Reload shaders.
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
SetPipeline(nullptr);
g_vertex_manager->InvalidatePipelineObject();
g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples);
return true;
}

@@ -688,6 +699,13 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
// Set default viewport and scissor, for the clear to work correctly
// New frame
stats.ResetFrame();
g_shader_cache->RetrieveAsyncShaders();

// We invalidate the pipeline object at the start of the frame.
// This is for the rare case where only a single pipeline configuration is used,
// and hybrid ubershaders have compiled the specialized shader, but without any
// state changes the specialized shader will not take over.
g_vertex_manager->InvalidatePipelineObject();

Core::Callback_VideoCopiedToXFB(true);
}
@@ -1009,3 +1027,8 @@ bool Renderer::UseVertexDepthRange() const
// in the vertex shader.
return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f;
}

std::unique_ptr<VideoCommon::AsyncShaderCompiler> Renderer::CreateAsyncShaderCompiler()
{
return std::make_unique<VideoCommon::AsyncShaderCompiler>();
}
@@ -28,6 +28,7 @@
#include "Common/Flag.h"
#include "Common/MathUtil.h"
#include "VideoCommon/AVIDump.h"
#include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/FPSCounter.h"
#include "VideoCommon/RenderState.h"
@@ -78,10 +79,7 @@ class Renderer
};

virtual void SetPipeline(const AbstractPipeline* pipeline) {}
virtual void SetBlendingState(const BlendingState& state) {}
virtual void SetScissorRect(const MathUtil::Rectangle<int>& rc) {}
virtual void SetRasterizationState(const RasterizationState& state) {}
virtual void SetDepthState(const DepthState& state) {}
virtual void SetTexture(u32 index, const AbstractTexture* texture) {}
virtual void SetSamplerState(u32 index, const SamplerState& state) {}
virtual void UnbindTexture(const AbstractTexture* texture) {}
@@ -189,6 +187,8 @@ class Renderer
void ResizeSurface(int new_width, int new_height);
bool UseVertexDepthRange() const;

virtual std::unique_ptr<VideoCommon::AsyncShaderCompiler> CreateAsyncShaderCompiler();

virtual void Shutdown();

// Drawing utility shaders.
@@ -243,6 +243,7 @@ class Renderer
std::mutex m_swap_mutex;

u32 m_last_host_config_bits = 0;
u32 m_last_efb_multisamples = 1;

private:
void RunFrameDumps();

Large diffs are not rendered by default.

@@ -0,0 +1,162 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <cstddef>
#include <cstring>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>

#include "Common/CommonTypes.h"
#include "Common/LinearDiskCache.h"

#include "VideoCommon/AbstractPipeline.h"
#include "VideoCommon/AbstractShader.h"
#include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/GXPipelineTypes.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/UberShaderPixel.h"
#include "VideoCommon/UberShaderVertex.h"
#include "VideoCommon/VertexShaderGen.h"

class NativeVertexFormat;

namespace VideoCommon
{
class ShaderCache final
{
public:
ShaderCache();
~ShaderCache();

// Perform at startup, create descriptor layouts, compiles all static shaders.
bool Initialize();
void Shutdown();

// Changes the shader host config. Shaders will be reloaded if there are changes.
void SetHostConfig(const ShaderHostConfig& host_config, u32 efb_multisamples);

// Reloads/recreates all shaders and pipelines.
void Reload();

// Retrieves all pending shaders/pipelines from the async compiler.
void RetrieveAsyncShaders();

// Get utility shader header based on current config.
std::string GetUtilityShaderHeader() const;

// Accesses ShaderGen shader caches
const AbstractPipeline* GetPipelineForUid(const GXPipelineUid& uid);
const AbstractPipeline* GetUberPipelineForUid(const GXUberPipelineUid& uid);

// Accesses ShaderGen shader caches asynchronously.
// The optional will be empty if this pipeline is now background compiling.
std::optional<const AbstractPipeline*> GetPipelineForUidAsync(const GXPipelineUid& uid);

private:
void WaitForAsyncCompiler();
void LoadShaderCaches();
void ClearShaderCaches();
void LoadPipelineUIDCache();
void CompileMissingPipelines();
void InvalidateCachedPipelines();
void ClearPipelineCaches();
void PrecompileUberShaders();

// GX shader compiler methods
std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid) const;
std::unique_ptr<AbstractShader>
CompileVertexUberShader(const UberShader::VertexShaderUid& uid) const;
std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid) const;
std::unique_ptr<AbstractShader>
CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const;
const AbstractShader* InsertVertexShader(const VertexShaderUid& uid,
std::unique_ptr<AbstractShader> shader);
const AbstractShader* InsertVertexUberShader(const UberShader::VertexShaderUid& uid,
std::unique_ptr<AbstractShader> shader);
const AbstractShader* InsertPixelShader(const PixelShaderUid& uid,
std::unique_ptr<AbstractShader> shader);
const AbstractShader* InsertPixelUberShader(const UberShader::PixelShaderUid& uid,
std::unique_ptr<AbstractShader> shader);
const AbstractShader* CreateGeometryShader(const GeometryShaderUid& uid);
bool NeedsGeometryShader(const GeometryShaderUid& uid) const;

// GX pipeline compiler methods
AbstractPipelineConfig
GetGXPipelineConfig(const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader,
const AbstractShader* geometry_shader, const AbstractShader* pixel_shader,
const RasterizationState& rasterization_state, const DepthState& depth_state,
const BlendingState& blending_state);
std::optional<AbstractPipelineConfig> GetGXPipelineConfig(const GXPipelineUid& uid);
std::optional<AbstractPipelineConfig> GetGXUberPipelineConfig(const GXUberPipelineUid& uid);
const AbstractPipeline* InsertGXPipeline(const GXPipelineUid& config,
std::unique_ptr<AbstractPipeline> pipeline);
const AbstractPipeline* InsertGXUberPipeline(const GXUberPipelineUid& config,
std::unique_ptr<AbstractPipeline> pipeline);
void AppendGXPipelineUID(const GXPipelineUid& config);

// ASync Compiler Methods
void QueueVertexShaderCompile(const VertexShaderUid& uid);
void QueueVertexUberShaderCompile(const UberShader::VertexShaderUid& uid);
void QueuePixelShaderCompile(const PixelShaderUid& uid);
void QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& uid);
void QueuePipelineCompile(const GXPipelineUid& uid);
void QueueUberPipelineCompile(const GXUberPipelineUid& uid);

// Configuration bits.
APIType m_api_type = APIType::Nothing;
ShaderHostConfig m_host_config = {};
u32 m_efb_multisamples = 1;
std::unique_ptr<AsyncShaderCompiler> m_async_shader_compiler;

// GX Shader Caches
template <typename Uid>
struct ShaderModuleCache
{
struct Shader
{
std::unique_ptr<AbstractShader> shader;
bool pending;
};
std::map<Uid, Shader> shader_map;
LinearDiskCache<Uid, u8> disk_cache;
};
ShaderModuleCache<VertexShaderUid> m_vs_cache;
ShaderModuleCache<GeometryShaderUid> m_gs_cache;
ShaderModuleCache<PixelShaderUid> m_ps_cache;
ShaderModuleCache<UberShader::VertexShaderUid> m_uber_vs_cache;
ShaderModuleCache<UberShader::PixelShaderUid> m_uber_ps_cache;

// GX Pipeline Caches - .first - pipeline, .second - pending
std::map<GXPipelineUid, std::pair<std::unique_ptr<AbstractPipeline>, bool>> m_gx_pipeline_cache;
std::map<GXUberPipelineUid, std::pair<std::unique_ptr<AbstractPipeline>, bool>>
m_gx_uber_pipeline_cache;

// Disk cache of pipeline UIDs
// We can't use the whole UID as a type
struct GXPipelineDiskCacheUid
{
PortableVertexDeclaration vertex_decl;
VertexShaderUid vs_uid;
GeometryShaderUid gs_uid;
PixelShaderUid ps_uid;
u32 rasterization_state_bits;
u32 depth_state_bits;
u32 blending_state_bits;
};
LinearDiskCache<GXPipelineDiskCacheUid, u8> m_gx_pipeline_uid_disk_cache;
};

} // namespace VideoCommon

extern std::unique_ptr<VideoCommon::ShaderCache> g_shader_cache;
@@ -37,28 +37,31 @@ ShaderHostConfig ShaderHostConfig::GetCurrent()
}

std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid,
bool include_host_config)
bool include_host_config, bool include_api)
{
if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));

std::string filename = File::GetUserPath(D_SHADERCACHE_IDX);
switch (api_type)
if (include_api)
{
case APIType::D3D:
filename += "D3D";
break;
case APIType::OpenGL:
filename += "OpenGL";
break;
case APIType::Vulkan:
filename += "Vulkan";
break;
default:
break;
switch (api_type)
{
case APIType::D3D:
filename += "D3D";
break;
case APIType::OpenGL:
filename += "OpenGL";
break;
case APIType::Vulkan:
filename += "Vulkan";
break;
default:
break;
}
filename += '-';
}

filename += '-';
filename += type;

if (include_gameid)
@@ -187,7 +187,7 @@ union ShaderHostConfig

// Gets the filename of the specified type of cache object (e.g. vertex shader, pipeline).
std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid,
bool include_host_config);
bool include_host_config, bool include_api = true);

template <class T>
inline void DefineOutputMember(T& object, APIType api_type, const char* qualifier, const char* type,
@@ -105,10 +105,8 @@ DataReader VertexManagerBase::PrepareForAdditionalData(int primitive, u32 count,
Flush();

// Have to update the rasterization state for point/line cull modes.
RasterizationState raster_state = {};
raster_state.Generate(bpmem, new_primitive_type);
g_renderer->SetRasterizationState(raster_state);
m_current_primitive_type = new_primitive_type;
SetRasterizationStateChanged();
}

// Check for size in buffer, if the buffer gets full, call Flush()
@@ -386,6 +384,10 @@ void VertexManagerBase::Flush()

if (!m_cull_all)
{
// Update the pipeline, or compile one if needed.
UpdatePipelineConfig();
UpdatePipelineObject();

// set the rest of the global constants
GeometryShaderManager::SetConstants();
PixelShaderManager::SetConstants();
@@ -477,3 +479,111 @@ void VertexManagerBase::CalculateZSlope(NativeVertexFormat* format)
m_zslope.f0 = out[2] - (out[0] * m_zslope.dfdx + out[1] * m_zslope.dfdy);
m_zslope.dirty = true;
}

void VertexManagerBase::UpdatePipelineConfig()
{
NativeVertexFormat* vertex_format = VertexLoaderManager::GetCurrentVertexFormat();
if (vertex_format != m_current_pipeline_config.vertex_format)
{
m_current_pipeline_config.vertex_format = vertex_format;
m_current_uber_pipeline_config.vertex_format =
VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration());
m_pipeline_config_changed = true;
}

VertexShaderUid vs_uid = GetVertexShaderUid();
if (vs_uid != m_current_pipeline_config.vs_uid)
{
m_current_pipeline_config.vs_uid = vs_uid;
m_current_uber_pipeline_config.vs_uid = UberShader::GetVertexShaderUid();
m_pipeline_config_changed = true;
}

PixelShaderUid ps_uid = GetPixelShaderUid();
if (ps_uid != m_current_pipeline_config.ps_uid)
{
m_current_pipeline_config.ps_uid = ps_uid;
m_current_uber_pipeline_config.ps_uid = UberShader::GetPixelShaderUid();
m_pipeline_config_changed = true;
}

GeometryShaderUid gs_uid = GetGeometryShaderUid(GetCurrentPrimitiveType());
if (gs_uid != m_current_pipeline_config.gs_uid)
{
m_current_pipeline_config.gs_uid = gs_uid;
m_current_uber_pipeline_config.gs_uid = gs_uid;
m_pipeline_config_changed = true;
}

if (m_rasterization_state_changed)
{
m_rasterization_state_changed = false;

RasterizationState new_rs = {};
new_rs.Generate(bpmem, m_current_primitive_type);
if (new_rs != m_current_pipeline_config.rasterization_state)
{
m_current_pipeline_config.rasterization_state = new_rs;
m_current_uber_pipeline_config.rasterization_state = new_rs;
m_pipeline_config_changed = true;
}
}

if (m_depth_state_changed)
{
m_depth_state_changed = false;

DepthState new_ds = {};
new_ds.Generate(bpmem);
if (new_ds != m_current_pipeline_config.depth_state)
{
m_current_pipeline_config.depth_state = new_ds;
m_current_uber_pipeline_config.depth_state = new_ds;
m_pipeline_config_changed = true;
}
}

if (m_blending_state_changed)
{
m_blending_state_changed = false;

BlendingState new_bs = {};
new_bs.Generate(bpmem);
if (new_bs != m_current_pipeline_config.blending_state)
{
m_current_pipeline_config.blending_state = new_bs;
m_current_uber_pipeline_config.blending_state = new_bs;
m_pipeline_config_changed = true;
}
}
}

void VertexManagerBase::UpdatePipelineObject()
{
if (!m_pipeline_config_changed)
return;

m_current_pipeline_object = nullptr;
m_pipeline_config_changed = false;

if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Disabled)
{
// Ubershaders disabled? Block and compile the specialized shader.
m_current_pipeline_object = g_shader_cache->GetPipelineForUid(m_current_pipeline_config);
return;
}
else if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Hybrid)
{
// Can we background compile shaders? If so, get the pipeline asynchronously.
auto res = g_shader_cache->GetPipelineForUidAsync(m_current_pipeline_config);
if (res)
{
// Specialized shaders are ready, prefer these.
m_current_pipeline_object = *res;
return;
}
}

// Exclusive ubershader mode, or hybrid and shaders are still compiling.
m_current_pipeline_object = g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config);
}
@@ -10,6 +10,7 @@
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/ShaderCache.h"

class DataReader;
class NativeVertexFormat;
@@ -58,6 +59,16 @@ class VertexManagerBase

std::pair<size_t, size_t> ResetFlushAspectRatioCount();

// State setters, called from register update functions.
void SetRasterizationStateChanged() { m_rasterization_state_changed = true; }
void SetDepthStateChanged() { m_depth_state_changed = true; }
void SetBlendingStateChanged() { m_blending_state_changed = true; }
void InvalidatePipelineObject()
{
m_current_pipeline_object = nullptr;
m_pipeline_config_changed = true;
}

protected:
virtual void vDoState(PointerWrap& p) {}
virtual void ResetBuffer(u32 stride) = 0;
@@ -72,8 +83,15 @@ class VertexManagerBase
Slope m_zslope = {};
void CalculateZSlope(NativeVertexFormat* format);

bool m_cull_all = false;
VideoCommon::GXPipelineUid m_current_pipeline_config;
VideoCommon::GXUberPipelineUid m_current_uber_pipeline_config;
const AbstractPipeline* m_current_pipeline_object = nullptr;
PrimitiveType m_current_primitive_type = PrimitiveType::Points;
bool m_pipeline_config_changed = true;
bool m_rasterization_state_changed = true;
bool m_depth_state_changed = true;
bool m_blending_state_changed = true;
bool m_cull_all = false;

private:
bool m_is_flushed = true;
@@ -84,6 +102,8 @@ class VertexManagerBase

virtual void CreateDeviceObjects() {}
virtual void DestroyDeviceObjects() {}
void UpdatePipelineConfig();
void UpdatePipelineObject();
};

extern std::unique_ptr<VertexManagerBase> g_vertex_manager;
@@ -68,6 +68,7 @@
<ClCompile Include="RenderBase.cpp" />
<ClCompile Include="RenderState.cpp" />
<ClCompile Include="LightingShaderGen.cpp" />
<ClCompile Include="ShaderCache.cpp" />
<ClCompile Include="ShaderGenCommon.cpp" />
<ClCompile Include="UberShaderCommon.cpp" />
<ClCompile Include="UberShaderPixel.cpp" />
@@ -119,6 +120,8 @@
<ClInclude Include="Fifo.h" />
<ClInclude Include="FPSCounter.h" />
<ClInclude Include="FramebufferManagerBase.h" />
<ClInclude Include="GXPipelineTypes.h" />
<ClInclude Include="ShaderCache.h" />
<ClInclude Include="UberShaderCommon.h" />
<ClInclude Include="UberShaderPixel.h" />
<ClInclude Include="HiresTextures.h" />
@@ -197,6 +197,9 @@
<ClCompile Include="AbstractFramebuffer.cpp">
<Filter>Base</Filter>
</ClCompile>
<ClCompile Include="ShaderCache.cpp">
<Filter>Shader Generators</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CommandProcessor.h" />
@@ -378,8 +381,14 @@
<ClInclude Include="AbstractFramebuffer.h">
<Filter>Base</Filter>
</ClInclude>
<ClInclude Include="GXPipelineTypes.h">
<Filter>Shader Generators</Filter>
</ClInclude>
<ClInclude Include="ShaderCache.h">
<Filter>Shader Generators</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>
</Project>
@@ -102,9 +102,8 @@ void VideoConfig::Refresh()
bBackendMultithreading = Config::Get(Config::GFX_BACKEND_MULTITHREADING);
iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL);
bShaderCache = Config::Get(Config::GFX_SHADER_CACHE);
bBackgroundShaderCompiling = Config::Get(Config::GFX_BACKGROUND_SHADER_COMPILING);
bDisableSpecializedShaders = Config::Get(Config::GFX_DISABLE_SPECIALIZED_SHADERS);
bPrecompileUberShaders = Config::Get(Config::GFX_PRECOMPILE_UBER_SHADERS);
bWaitForShadersBeforeStarting = Config::Get(Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING);
iUberShaderMode = static_cast<UberShaderMode>(Config::Get(Config::GFX_UBERSHADER_MODE));
iShaderCompilerThreads = Config::Get(Config::GFX_SHADER_COMPILER_THREADS);
iShaderPrecompilerThreads = Config::Get(Config::GFX_SHADER_PRECOMPILER_THREADS);

@@ -187,6 +186,9 @@ static u32 GetNumAutoShaderCompilerThreads()

u32 VideoConfig::GetShaderCompilerThreads() const
{
if (!backend_info.bSupportsBackgroundCompiling)
return 0;

if (iShaderCompilerThreads >= 0)
return static_cast<u32>(iShaderCompilerThreads);
else
@@ -195,20 +197,15 @@ u32 VideoConfig::GetShaderCompilerThreads() const

u32 VideoConfig::GetShaderPrecompilerThreads() const
{
// When using background compilation, always keep the same thread count.
if (bWaitForShadersBeforeStarting)
return GetShaderCompilerThreads();

if (!backend_info.bSupportsBackgroundCompiling)
return 0;

if (iShaderPrecompilerThreads >= 0)
return static_cast<u32>(iShaderPrecompilerThreads);
else
return GetNumAutoShaderCompilerThreads();
}

bool VideoConfig::CanPrecompileUberShaders() const
{
// We don't want to precompile ubershaders if they're never going to be used.
return bPrecompileUberShaders && (bBackgroundShaderCompiling || bDisableSpecializedShaders);
}

bool VideoConfig::CanBackgroundCompileShaders() const
{
// We require precompiled ubershaders to background compile shaders.
return bBackgroundShaderCompiling && bPrecompileUberShaders;
}
@@ -42,6 +42,13 @@ enum class StereoMode : int
Nvidia3DVision
};

enum class UberShaderMode : int
{
Disabled,
Hybrid,
Exclusive
};

struct ProjectionHackConfig final
{
bool m_enable;
@@ -161,25 +168,9 @@ struct VideoConfig final
// Currently only supported with Vulkan.
int iCommandBufferExecuteInterval;

// The following options determine the ubershader mode:
// No ubershaders:
// - bBackgroundShaderCompiling = false
// - bDisableSpecializedShaders = false
// Hybrid/background compiling:
// - bBackgroundShaderCompiling = true
// - bDisableSpecializedShaders = false
// Ubershaders only:
// - bBackgroundShaderCompiling = false
// - bDisableSpecializedShaders = true

// Enable background shader compiling, use ubershaders while waiting.
bool bBackgroundShaderCompiling;

// Use ubershaders only, don't compile specialized shaders.
bool bDisableSpecializedShaders;

// Precompile ubershader variants at boot/config reload time.
bool bPrecompileUberShaders;
// Shader compilation settings.
bool bWaitForShadersBeforeStarting;
UberShaderMode iUberShaderMode;

// Number of shader compiler threads.
// 0 disables background compilation.
@@ -227,6 +218,7 @@ struct VideoConfig final
bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon
bool bSupportsBPTCTextures;
bool bSupportsFramebufferFetch; // Used as an alternative to dual-source blend on GLES
bool bSupportsBackgroundCompiling;
} backend_info;

// Utility
@@ -246,10 +238,9 @@ struct VideoConfig final
return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding;
}
bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; }
bool UsingUberShaders() const { return iUberShaderMode != UberShaderMode::Disabled; }
u32 GetShaderCompilerThreads() const;
u32 GetShaderPrecompilerThreads() const;
bool CanPrecompileUberShaders() const;
bool CanBackgroundCompileShaders() const;
};

extern VideoConfig g_Config;