Skip to content
Permalink
Browse files

Merge pull request #7999 from stenzek/pipeline-cache-data

Implement pipeline data cache for OpenGL and D3D12
  • Loading branch information...
stenzek committed Apr 20, 2019
2 parents 9e95181 + 00b83b4 commit c26f53bf84007bad8b6b89fae91e38f4cdb531bd
Showing with 425 additions and 179 deletions.
  1. +3 −1 Source/Core/VideoBackends/D3D/Render.cpp
  2. +3 −1 Source/Core/VideoBackends/D3D/Render.h
  3. +2 −0 Source/Core/VideoBackends/D3D/main.cpp
  4. +23 −2 Source/Core/VideoBackends/D3D12/DXPipeline.cpp
  5. +4 −1 Source/Core/VideoBackends/D3D12/DXPipeline.h
  6. +4 −2 Source/Core/VideoBackends/D3D12/Renderer.cpp
  7. +3 −1 Source/Core/VideoBackends/D3D12/Renderer.h
  8. +2 −0 Source/Core/VideoBackends/D3D12/VideoBackend.cpp
  9. +0 −5 Source/Core/VideoBackends/D3DCommon/Shader.cpp
  10. +0 −1 Source/Core/VideoBackends/D3DCommon/Shader.h
  11. +2 −0 Source/Core/VideoBackends/Null/NullBackend.cpp
  12. +3 −4 Source/Core/VideoBackends/Null/Render.cpp
  13. +3 −1 Source/Core/VideoBackends/Null/Render.h
  14. +39 −4 Source/Core/VideoBackends/OGL/OGLPipeline.cpp
  15. +5 −3 Source/Core/VideoBackends/OGL/OGLPipeline.h
  16. +0 −11 Source/Core/VideoBackends/OGL/OGLShader.cpp
  17. +0 −2 Source/Core/VideoBackends/OGL/OGLShader.h
  18. +70 −37 Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp
  19. +7 −5 Source/Core/VideoBackends/OGL/ProgramShaderCache.h
  20. +18 −5 Source/Core/VideoBackends/OGL/Render.cpp
  21. +3 −2 Source/Core/VideoBackends/OGL/Render.h
  22. +2 −0 Source/Core/VideoBackends/OGL/main.cpp
  23. +3 −2 Source/Core/VideoBackends/Software/SWRenderer.cpp
  24. +3 −1 Source/Core/VideoBackends/Software/SWRenderer.h
  25. +2 −0 Source/Core/VideoBackends/Software/SWmain.cpp
  26. +3 −1 Source/Core/VideoBackends/Vulkan/Renderer.cpp
  27. +3 −1 Source/Core/VideoBackends/Vulkan/Renderer.h
  28. +0 −6 Source/Core/VideoBackends/Vulkan/VKShader.cpp
  29. +0 −1 Source/Core/VideoBackends/Vulkan/VKShader.h
  30. +2 −0 Source/Core/VideoBackends/Vulkan/VulkanContext.cpp
  31. +6 −0 Source/Core/VideoCommon/AbstractPipeline.h
  32. +4 −2 Source/Core/VideoCommon/AbstractShader.h
  33. +10 −0 Source/Core/VideoCommon/GXPipelineTypes.h
  34. +3 −2 Source/Core/VideoCommon/RenderBase.h
  35. +172 −70 Source/Core/VideoCommon/ShaderCache.cpp
  36. +16 −5 Source/Core/VideoCommon/ShaderCache.h
  37. +2 −0 Source/Core/VideoCommon/VideoConfig.h
@@ -127,7 +127,9 @@ std::unique_ptr<AbstractShader> Renderer::CreateShaderFromBinary(ShaderStage sta
return DXShader::CreateFromBytecode(stage, DXShader::CreateByteCode(data, length));
}

std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config)
std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data,
size_t cache_data_length)
{
return DXPipeline::Create(config);
}
@@ -34,7 +34,9 @@ class Renderer : public ::Renderer
size_t length) override;
std::unique_ptr<NativeVertexFormat>
CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config) override;
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data = nullptr,
size_t cache_data_length = 0) override;
std::unique_ptr<AbstractFramebuffer>
CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) override;

@@ -82,6 +82,8 @@ void VideoBackend::FillBackendInfo()
g_Config.backend_info.bSupportsFragmentStoresAndAtomics = true;
g_Config.backend_info.bSupportsGSInstancing = true;
g_Config.backend_info.bSupportsSSAA = true;
g_Config.backend_info.bSupportsShaderBinaries = true;
g_Config.backend_info.bSupportsPipelineCacheData = false;

g_Config.backend_info.Adapters = D3DCommon::GetAdapterNames();
g_Config.backend_info.AAModes = D3D::GetAAModes(g_Config.iAdapter);
@@ -156,7 +156,8 @@ static void GetD3DBlendDesc(D3D12_BLEND_DESC* desc, const BlendingState& state)
}
}

std::unique_ptr<DXPipeline> DXPipeline::Create(const AbstractPipelineConfig& config)
std::unique_ptr<DXPipeline> DXPipeline::Create(const AbstractPipelineConfig& config,
const void* cache_data, size_t cache_data_size)
{
DEBUG_ASSERT(config.vertex_shader && config.pixel_shader);

@@ -202,16 +203,36 @@ std::unique_ptr<DXPipeline> DXPipeline::Create(const AbstractPipelineConfig& con
D3DCommon::GetDSVFormatForAbstractFormat(config.framebuffer_state.depth_texture_format);
desc.SampleDesc.Count = config.framebuffer_state.samples;
desc.NodeMask = 1;
desc.CachedPSO.pCachedBlob = cache_data;
desc.CachedPSO.CachedBlobSizeInBytes = cache_data_size;

ID3D12PipelineState* pso;
HRESULT hr = g_dx_context->GetDevice()->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&pso));
CHECK(SUCCEEDED(hr), "Create PSO");
if (FAILED(hr))
{
WARN_LOG(VIDEO, "CreateGraphicsPipelineState() %sfailed with HRESULT %08X",
cache_data ? "with cache data " : "", hr);
return nullptr;
}

const bool use_integer_rtv =
!config.blending_state.blendenable && config.blending_state.logicopenable;
return std::make_unique<DXPipeline>(pso, desc.pRootSignature, config.usage,
GetD3DTopology(config.rasterization_state), use_integer_rtv);
}

AbstractPipeline::CacheData DXPipeline::GetCacheData() const
{
ComPtr<ID3DBlob> blob;
HRESULT hr = m_pipeline->GetCachedBlob(&blob);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "ID3D12Pipeline::GetCachedBlob() failed with HRESULT %08X", hr);
return {};
}

CacheData data(blob->GetBufferSize());
std::memcpy(data.data(), blob->GetBufferPointer(), blob->GetBufferSize());
return data;
}
} // namespace DX12
@@ -19,14 +19,17 @@ class DXPipeline final : public AbstractPipeline
bool use_integer_rtv);
~DXPipeline() override;

static std::unique_ptr<DXPipeline> Create(const AbstractPipelineConfig& config);
static std::unique_ptr<DXPipeline> Create(const AbstractPipelineConfig& config,
const void* cache_data, size_t cache_data_size);

ID3D12PipelineState* GetPipeline() const { return m_pipeline; }
ID3D12RootSignature* GetRootSignature() const { return m_root_signature; }
AbstractPipelineUsage GetUsage() const { return m_usage; }
D3D12_PRIMITIVE_TOPOLOGY GetPrimitiveTopology() const { return m_primitive_topology; }
bool UseIntegerRTV() const { return m_use_integer_rtv; }

CacheData GetCacheData() const override;

private:
ID3D12PipelineState* m_pipeline;
ID3D12RootSignature* m_root_signature;
@@ -99,9 +99,11 @@ Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
return std::make_unique<DXVertexFormat>(vtx_decl);
}

std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config)
std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data,
size_t cache_data_length)
{
return DXPipeline::Create(config);
return DXPipeline::Create(config, cache_data, cache_data_length);
}

u16 Renderer::BBoxRead(int index)
@@ -41,7 +41,9 @@ class Renderer final : public ::Renderer
size_t length) override;
std::unique_ptr<NativeVertexFormat>
CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config) override;
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data = nullptr,
size_t cache_data_length = 0) override;

u16 BBoxRead(int index) override;
void BBoxWrite(int index, u16 value) override;
@@ -80,6 +80,8 @@ void VideoBackend::FillBackendInfo()
g_Config.backend_info.bSupportsPartialDepthCopies = false;
g_Config.backend_info.Adapters = D3DCommon::GetAdapterNames();
g_Config.backend_info.AAModes = DXContext::GetAAModes(g_Config.iAdapter);
g_Config.backend_info.bSupportsShaderBinaries = true;
g_Config.backend_info.bSupportsPipelineCacheData = true;

// We can only check texture support once we have a device.
if (g_dx_context)
@@ -23,11 +23,6 @@ Shader::Shader(ShaderStage stage, BinaryData bytecode)

Shader::~Shader() = default;

bool Shader::HasBinary() const
{
return true;
}

AbstractShader::BinaryData Shader::GetBinary() const
{
return m_bytecode;
@@ -16,7 +16,6 @@ class Shader : public AbstractShader

const BinaryData& GetByteCode() const { return m_bytecode; }

bool HasBinary() const override;
BinaryData GetBinary() const override;

static bool CompileShader(D3D_FEATURE_LEVEL feature_level, BinaryData* out_bytecode,
@@ -52,6 +52,8 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsLogicOp = false;
g_Config.backend_info.bSupportsLargePoints = false;
g_Config.backend_info.bSupportsPartialDepthCopies = false;
g_Config.backend_info.bSupportsShaderBinaries = false;
g_Config.backend_info.bSupportsPipelineCacheData = false;

// aamodes: We only support 1 sample, so no MSAA
g_Config.backend_info.Adapters.clear();
@@ -46,9 +46,6 @@ class NullShader final : public AbstractShader
public:
explicit NullShader(ShaderStage stage) : AbstractShader(stage) {}
~NullShader() = default;

bool HasBinary() const override { return false; }
BinaryData GetBinary() const override { return {}; }
};

std::unique_ptr<AbstractShader> Renderer::CreateShaderFromSource(ShaderStage stage,
@@ -70,7 +67,9 @@ class NullPipeline final : public AbstractPipeline
~NullPipeline() override = default;
};

std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config)
std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data,
size_t cache_data_length)
{
return std::make_unique<NullPipeline>();
}
@@ -28,7 +28,9 @@ class Renderer : public ::Renderer
size_t length) override;
std::unique_ptr<NativeVertexFormat>
CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config) override;
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config,
const void* cache_data = nullptr,
size_t cache_data_length = 0) override;

u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override { return 0; }
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {}
@@ -9,6 +9,7 @@
#include "VideoBackends/OGL/ProgramShaderCache.h"
#include "VideoBackends/OGL/Render.h"
#include "VideoBackends/OGL/VertexManager.h"
#include "VideoCommon/VideoConfig.h"

namespace OGL
{
@@ -31,7 +32,7 @@ static GLenum MapToGLPrimitive(PrimitiveType primitive_type)
OGLPipeline::OGLPipeline(const GLVertexFormat* vertex_format,
const RasterizationState& rasterization_state,
const DepthState& depth_state, const BlendingState& blending_state,
const PipelineProgram* program, GLuint gl_primitive)
PipelineProgram* program, GLuint gl_primitive)
: m_vertex_format(vertex_format), m_rasterization_state(rasterization_state),
m_depth_state(depth_state), m_blending_state(blending_state), m_program(program),
m_gl_primitive(gl_primitive)
@@ -44,13 +45,47 @@ OGLPipeline::~OGLPipeline()
ProgramShaderCache::ReleasePipelineProgram(m_program);
}

std::unique_ptr<OGLPipeline> OGLPipeline::Create(const AbstractPipelineConfig& config)
AbstractPipeline::CacheData OGLPipeline::GetCacheData() const
{
const PipelineProgram* program = ProgramShaderCache::GetPipelineProgram(
// More than one pipeline can share the same shaders. To avoid bloating the cache with multiple
// copies of the same program combination, we set a flag on the program object so that it can't
// be retrieved again. When booting, the pipeline cache is loaded in-order, so the additional
// pipelines which use the program combination will re-use the already-created object.
if (!g_ActiveConfig.backend_info.bSupportsPipelineCacheData || m_program->binary_retrieved)
return {};

GLint program_size = 0;
glGetProgramiv(m_program->shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &program_size);
if (program_size == 0)
return {};

// Clear any existing error.
glGetError();

// We pack the format at the start of the buffer.
CacheData data(program_size + sizeof(u32));
GLsizei data_size = 0;
GLenum program_format = 0;
glGetProgramBinary(m_program->shader.glprogid, program_size, &data_size, &program_format,
&data[sizeof(u32)]);
if (glGetError() != GL_NO_ERROR || data_size == 0)
return {};

u32 program_format_u32 = static_cast<u32>(program_format);
std::memcpy(&data[0], &program_format_u32, sizeof(u32));
data.resize(data_size + sizeof(u32));
m_program->binary_retrieved = true;
return data;
}

std::unique_ptr<OGLPipeline> OGLPipeline::Create(const AbstractPipelineConfig& config,
const void* cache_data, size_t cache_data_size)
{
PipelineProgram* program = ProgramShaderCache::GetPipelineProgram(
static_cast<const GLVertexFormat*>(config.vertex_format),
static_cast<const OGLShader*>(config.vertex_shader),
static_cast<const OGLShader*>(config.geometry_shader),
static_cast<const OGLShader*>(config.pixel_shader));
static_cast<const OGLShader*>(config.pixel_shader), cache_data, cache_data_size);
if (!program)
return nullptr;

@@ -18,7 +18,7 @@ class OGLPipeline final : public AbstractPipeline
public:
explicit OGLPipeline(const GLVertexFormat* vertex_format,
const RasterizationState& rasterization_state, const DepthState& depth_state,
const BlendingState& blending_state, const PipelineProgram* program,
const BlendingState& blending_state, PipelineProgram* program,
GLenum gl_primitive);
~OGLPipeline() override;

@@ -29,14 +29,16 @@ class OGLPipeline final : public AbstractPipeline
const PipelineProgram* GetProgram() const { return m_program; }
bool HasVertexInput() const { return m_vertex_format != nullptr; }
GLenum GetGLPrimitive() const { return m_gl_primitive; }
static std::unique_ptr<OGLPipeline> Create(const AbstractPipelineConfig& config);
CacheData GetCacheData() const override;
static std::unique_ptr<OGLPipeline> Create(const AbstractPipelineConfig& config,
const void* cache_data, size_t cache_data_size);

private:
const GLVertexFormat* m_vertex_format;
RasterizationState m_rasterization_state;
DepthState m_depth_state;
BlendingState m_blending_state;
const PipelineProgram* m_program;
PipelineProgram* m_program;
GLenum m_gl_primitive;
};

@@ -44,17 +44,6 @@ OGLShader::~OGLShader()
glDeleteProgram(m_gl_compute_program_id);
}

bool OGLShader::HasBinary() const
{
// NOTE: GL shaders do not have binaries, programs do.
return false;
}

AbstractShader::BinaryData OGLShader::GetBinary() const
{
return {};
}

std::unique_ptr<OGLShader> OGLShader::CreateFromSource(ShaderStage stage, const char* source,
size_t length)
{
@@ -24,8 +24,6 @@ class OGLShader final : public AbstractShader
GLenum GetGLShaderType() const { return m_type; }
GLuint GetGLShaderID() const { return m_gl_id; }
GLuint GetGLComputeProgramID() const { return m_gl_compute_program_id; }
bool HasBinary() const override;
BinaryData GetBinary() const override;

static std::unique_ptr<OGLShader> CreateFromSource(ShaderStage stage, const char* source,
size_t length);

0 comments on commit c26f53b

Please sign in to comment.
You can’t perform that action at this time.