mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
Shader compilation defaults to Async (UberShaders): a draw whose own pipeline is not built yet is drawn with an ubershader one, exact rendering, while its own builds on the compiler threads, which now number as many as the machine has to spare (cores - 3, 1 to 4) instead of one. What is left to stutter is an ubershader pipeline itself, built on the spot the first time a draw needs one: one per vertex layout and fixed function state. Every ubershader pipeline a draw uses is now recorded, in <game id>.uberuidcache next to the specialized UID cache, and those are built before the game starts the next time it boots; every other ubershader pipeline there can be, and the specialized pipelines known, build in the background while it runs instead of before it starts, which took 21 s on lavapipe. The record is written when the cache closes, into room reserved when it opens, so recording allocates nothing in the middle of a game. Without compiler threads (OpenGL here, with no shared context for them), asking for a pipeline in the background builds it on the spot and an ubershader one besides: the asynchronous modes draw as Synchronous does then, and nothing extra is built before the game starts. Booting a FIFO log no longer hangs: the CPU thread clears the EFB, which waits on the GPU, before it reaches its run loop, and the frontend's thread waited for that run loop without running the GPU. It runs the GPU until then.
285 lines
12 KiB
C++
285 lines
12 KiB
C++
// Copyright 2018 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <map>
|
|
#include <set>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#ifdef __LIBRETRO__
|
|
#include <vector>
|
|
#endif
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/IOFile.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/PixelShaderGen.h"
|
|
#include "VideoCommon/RenderState.h"
|
|
#include "VideoCommon/TextureCacheBase.h"
|
|
#include "VideoCommon/TextureConversionShader.h"
|
|
#include "VideoCommon/TextureConverterShaderGen.h"
|
|
#include "VideoCommon/UberShaderPixel.h"
|
|
#include "VideoCommon/UberShaderVertex.h"
|
|
#include "VideoCommon/VertexShaderGen.h"
|
|
#include "VideoCommon/VideoEvents.h"
|
|
|
|
class NativeVertexFormat;
|
|
enum class AbstractTextureFormat : u32;
|
|
enum class APIType;
|
|
enum class TextureFormat;
|
|
enum class TLUTFormat;
|
|
|
|
namespace VideoCommon
|
|
{
|
|
class ShaderCache final
|
|
{
|
|
public:
|
|
/* Whether pipelines can build on threads of their own: without, asking
|
|
* for one in the background builds it on the spot all the same. */
|
|
bool CanCompileInBackground() const;
|
|
|
|
ShaderCache();
|
|
~ShaderCache();
|
|
|
|
// Perform at startup, create descriptor layouts, compiles all static shaders.
|
|
bool Initialize();
|
|
void Shutdown();
|
|
|
|
// Compiles/loads cached shaders.
|
|
void InitializeShaderCache();
|
|
|
|
// Changes the shader host config. Shaders should be reloaded afterwards.
|
|
void SetHostConfig(const ShaderHostConfig& host_config) { m_host_config.bits = host_config.bits; }
|
|
|
|
// Reloads/recreates all shaders and pipelines.
|
|
void Reload();
|
|
|
|
// Retrieves all pending shaders/pipelines from the async compiler.
|
|
void RetrieveAsyncShaders();
|
|
|
|
// 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);
|
|
|
|
// Shared shaders
|
|
const AbstractShader* GetScreenQuadVertexShader() const
|
|
{
|
|
return m_screen_quad_vertex_shader.get();
|
|
}
|
|
const AbstractShader* GetTextureCopyVertexShader() const
|
|
{
|
|
return m_texture_copy_vertex_shader.get();
|
|
}
|
|
const AbstractShader* GetEFBCopyVertexShader() const { return m_efb_copy_vertex_shader.get(); }
|
|
const AbstractShader* GetTexcoordGeometryShader() const
|
|
{
|
|
return m_texcoord_geometry_shader.get();
|
|
}
|
|
const AbstractShader* GetTextureCopyPixelShader() const
|
|
{
|
|
return m_texture_copy_pixel_shader.get();
|
|
}
|
|
const AbstractShader* GetColorGeometryShader() const { return m_color_geometry_shader.get(); }
|
|
const AbstractShader* GetColorPixelShader() const { return m_color_pixel_shader.get(); }
|
|
|
|
// EFB copy to RAM/VRAM pipelines
|
|
const AbstractPipeline*
|
|
GetEFBCopyToVRAMPipeline(const TextureConversionShaderGen::TCShaderUid& uid);
|
|
const AbstractPipeline* GetEFBCopyToRAMPipeline(const EFBCopyParams& uid);
|
|
|
|
// RGBA8 framebuffer copy pipelines
|
|
const AbstractPipeline* GetRGBA8CopyPipeline() const { return m_copy_rgba8_pipeline.get(); }
|
|
const AbstractPipeline* GetRGBA8StereoCopyPipeline() const
|
|
{
|
|
return m_rgba8_stereo_copy_pipeline.get();
|
|
}
|
|
|
|
// Palette texture conversion pipelines
|
|
const AbstractPipeline* GetPaletteConversionPipeline(TLUTFormat format);
|
|
|
|
// Texture reinterpret pipelines
|
|
const AbstractPipeline* GetTextureReinterpretPipeline(TextureFormat from_format,
|
|
TextureFormat to_format);
|
|
|
|
// Texture decoding compute shaders
|
|
const AbstractShader* GetTextureDecodingShader(TextureFormat format,
|
|
std::optional<TLUTFormat> palette_format);
|
|
|
|
private:
|
|
static constexpr size_t NUM_PALETTE_CONVERSION_SHADERS = 3;
|
|
|
|
void WaitForAsyncCompiler();
|
|
void LoadCaches();
|
|
void ClearCaches();
|
|
void LoadPipelineUIDCache();
|
|
void ClosePipelineUIDCache();
|
|
void CompileMissingPipelines();
|
|
void QueueUberShaderPipelines();
|
|
bool CompileSharedPipelines();
|
|
|
|
// 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;
|
|
|
|
// Should we use geometry shaders for EFB copies?
|
|
bool UseGeometryShaderForEFBCopies() 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, AbstractPipelineUsage usage);
|
|
std::optional<AbstractPipelineConfig> GetGXPipelineConfig(const GXPipelineUid& uid);
|
|
std::optional<AbstractPipelineConfig> GetGXPipelineConfig(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 AddSerializedGXPipelineUID(const SerializedGXPipelineUid& uid);
|
|
void AppendGXPipelineUID(const GXPipelineUid& config);
|
|
#ifdef __LIBRETRO__
|
|
void LoadUberPipelineUIDCache();
|
|
void AppendGXUberPipelineUID(const GXUberPipelineUid& config);
|
|
/* queues the ubershader pipelines known and not built yet */
|
|
void CompileMissingUberPipelines();
|
|
#endif
|
|
|
|
// ASync Compiler Methods
|
|
void QueueVertexShaderCompile(const VertexShaderUid& uid, u32 priority);
|
|
void QueueVertexUberShaderCompile(const UberShader::VertexShaderUid& uid, u32 priority);
|
|
void QueuePixelShaderCompile(const PixelShaderUid& uid, u32 priority);
|
|
void QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& uid, u32 priority);
|
|
void QueuePipelineCompile(const GXPipelineUid& uid, u32 priority);
|
|
void QueueUberPipelineCompile(const GXUberPipelineUid& uid, u32 priority);
|
|
|
|
// Populating various caches.
|
|
template <ShaderStage stage, typename K, typename T>
|
|
void LoadShaderCache(T& cache, APIType api_type, const char* type, bool include_gameid);
|
|
template <typename T>
|
|
void ClearShaderCache(T& cache);
|
|
template <typename KeyType, typename DiskKeyType, typename T>
|
|
void LoadPipelineCache(T& cache, Common::LinearDiskCache<DiskKeyType, u8>& disk_cache,
|
|
APIType api_type, const char* type, bool include_gameid);
|
|
template <typename T, typename Y>
|
|
void ClearPipelineCache(T& cache, Y& disk_cache);
|
|
|
|
// Priorities for compiling. The lower the value, the sooner the pipeline is compiled.
|
|
// The shader cache is compiled last, as it is the least likely to be required. On demand
|
|
// shaders are always compiled before pending ubershaders, as we want to use the ubershader
|
|
// for as few frames as possible, otherwise we risk framerate drops.
|
|
enum : u32
|
|
{
|
|
COMPILE_PRIORITY_ONDEMAND_PIPELINE = 100,
|
|
COMPILE_PRIORITY_UBERSHADER_PIPELINE = 200,
|
|
COMPILE_PRIORITY_SHADERCACHE_PIPELINE = 300
|
|
};
|
|
|
|
// Configuration bits.
|
|
APIType m_api_type;
|
|
ShaderHostConfig m_host_config = {};
|
|
std::unique_ptr<AsyncShaderCompiler> m_async_shader_compiler;
|
|
|
|
// Shared shaders
|
|
std::unique_ptr<AbstractShader> m_screen_quad_vertex_shader;
|
|
std::unique_ptr<AbstractShader> m_texture_copy_vertex_shader;
|
|
std::unique_ptr<AbstractShader> m_efb_copy_vertex_shader;
|
|
std::unique_ptr<AbstractShader> m_texcoord_geometry_shader;
|
|
std::unique_ptr<AbstractShader> m_color_geometry_shader;
|
|
std::unique_ptr<AbstractShader> m_texture_copy_pixel_shader;
|
|
std::unique_ptr<AbstractShader> m_color_pixel_shader;
|
|
|
|
// GX Shader Caches
|
|
template <typename Uid>
|
|
struct ShaderModuleCache
|
|
{
|
|
struct Shader
|
|
{
|
|
std::unique_ptr<AbstractShader> shader;
|
|
bool pending = false;
|
|
};
|
|
std::map<Uid, Shader> shader_map;
|
|
Common::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;
|
|
File::IOFile m_gx_pipeline_uid_cache_file;
|
|
#ifdef __LIBRETRO__
|
|
std::vector<SerializedGXPipelineUid> m_pending_gx_pipeline_uids;
|
|
/* The ubershader pipelines a game drew with, as for the specialized
|
|
* ones: compiled while the game boots next time, so a hybrid draw
|
|
* finds its ubershader ready instead of building it on the spot. */
|
|
File::IOFile m_gx_uber_pipeline_uid_cache_file;
|
|
std::vector<SerializedGXUberPipelineUid> m_pending_gx_uber_pipeline_uids;
|
|
/* those already in the file or written to it this session */
|
|
std::set<GXUberPipelineUid> m_recorded_uber_pipeline_uids;
|
|
#endif
|
|
Common::LinearDiskCache<SerializedGXPipelineUid, u8> m_gx_pipeline_disk_cache;
|
|
Common::LinearDiskCache<SerializedGXUberPipelineUid, u8> m_gx_uber_pipeline_disk_cache;
|
|
|
|
// EFB copy to VRAM/RAM pipelines
|
|
std::map<TextureConversionShaderGen::TCShaderUid, std::unique_ptr<AbstractPipeline>>
|
|
m_efb_copy_to_vram_pipelines;
|
|
std::map<EFBCopyParams, std::unique_ptr<AbstractPipeline>> m_efb_copy_to_ram_pipelines;
|
|
|
|
// Copy pipeline for RGBA8 textures
|
|
std::unique_ptr<AbstractPipeline> m_copy_rgba8_pipeline;
|
|
std::unique_ptr<AbstractPipeline> m_rgba8_stereo_copy_pipeline;
|
|
|
|
// Palette conversion pipelines
|
|
std::array<std::unique_ptr<AbstractPipeline>, NUM_PALETTE_CONVERSION_SHADERS>
|
|
m_palette_conversion_pipelines;
|
|
|
|
// Texture reinterpreting pipeline
|
|
std::map<std::pair<TextureFormat, TextureFormat>, std::unique_ptr<AbstractPipeline>>
|
|
m_texture_reinterpret_pipelines;
|
|
|
|
// Texture decoding shaders
|
|
std::map<std::pair<u32, u32>, std::unique_ptr<AbstractShader>> m_texture_decoding_shaders;
|
|
|
|
Common::EventHook m_frame_end_handler;
|
|
};
|
|
|
|
} // namespace VideoCommon
|
|
|
|
extern std::unique_ptr<VideoCommon::ShaderCache> g_shader_cache;
|