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

#include "Common/Assert.h"

#include "VideoBackends/Vulkan/ShaderCompiler.h"
#include "VideoBackends/Vulkan/Util.h"
#include "VideoBackends/Vulkan/VKShader.h"
#include "VideoBackends/Vulkan/VulkanContext.h"

namespace Vulkan
{
VKShader::VKShader(ShaderStage stage, std::vector<u32> spv, VkShaderModule mod)
: AbstractShader(stage), m_spv(std::move(spv)), m_module(mod),
m_compute_pipeline(VK_NULL_HANDLE)
{
}

VKShader::VKShader(std::vector<u32> spv, VkPipeline compute_pipeline)
: AbstractShader(ShaderStage::Compute), m_spv(std::move(spv)), m_module(VK_NULL_HANDLE),
m_compute_pipeline(compute_pipeline)
{
}

VKShader::~VKShader()
{
if (m_stage != ShaderStage::Compute)
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr);
else
vkDestroyPipeline(g_vulkan_context->GetDevice(), m_compute_pipeline, nullptr);
}

bool VKShader::HasBinary() const
{
_assert_(!m_spv.empty());
return true;
}

AbstractShader::BinaryData VKShader::GetBinary() const
{
BinaryData ret(sizeof(u32) * m_spv.size());
std::memcpy(ret.data(), m_spv.data(), sizeof(u32) * m_spv.size());
return ret;
}

static std::unique_ptr<VKShader> CreateShaderObject(ShaderStage stage,
ShaderCompiler::SPIRVCodeVector spv)
{
VkShaderModule mod = Util::CreateShaderModule(spv.data(), spv.size());
if (mod == VK_NULL_HANDLE)
return nullptr;

// If it's a graphics shader, we defer pipeline creation.
if (stage != ShaderStage::Compute)
return std::make_unique<VKShader>(stage, std::move(spv), mod);

// If it's a compute shader, we create the pipeline straight away.
ComputePipelineInfo pinfo;
pinfo.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_COMPUTE);
pinfo.cs = mod;
VkPipeline pipeline = g_shader_cache->CreateComputePipeline(pinfo);
if (pipeline == VK_NULL_HANDLE)
{
vkDestroyShaderModule(g_vulkan_context->GetDevice(), mod, nullptr);
return nullptr;
}

// Shader module is no longer needed, now it is compiled to a pipeline.
return std::make_unique<VKShader>(std::move(spv), pipeline);
}

std::unique_ptr<VKShader> VKShader::CreateFromSource(ShaderStage stage, const char* source,
size_t length)
{
ShaderCompiler::SPIRVCodeVector spv;
bool result;
switch (stage)
{
case ShaderStage::Vertex:
result = ShaderCompiler::CompileVertexShader(&spv, source, length);
break;
case ShaderStage::Geometry:
result = ShaderCompiler::CompileGeometryShader(&spv, source, length);
break;
case ShaderStage::Pixel:
result = ShaderCompiler::CompileFragmentShader(&spv, source, length);
break;
case ShaderStage::Compute:
result = ShaderCompiler::CompileComputeShader(&spv, source, length);
break;
default:
result = false;
break;
}

if (!result)
return nullptr;

return CreateShaderObject(stage, std::move(spv));
}

std::unique_ptr<VKShader> VKShader::CreateFromBinary(ShaderStage stage, const void* data,
size_t length)
{
ShaderCompiler::SPIRVCodeVector spv;
const size_t size_in_words = sizeof(length) / sizeof(ShaderCompiler::SPIRVCodeType);
if (size_in_words > 0)
{
spv.resize(length / size_in_words);
std::memcpy(spv.data(), data, size_in_words);
}

// Non-aligned code sizes, unlikely (unless using VK_NV_glsl).
if ((length % sizeof(ShaderCompiler::SPIRVCodeType)) != 0)
{
spv.resize(size_in_words + 1);
std::memcpy(&spv[size_in_words], data, (length % sizeof(ShaderCompiler::SPIRVCodeType)));
}

return CreateShaderObject(stage, std::move(spv));
}

} // namespace Vulkan
@@ -0,0 +1,40 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <cstddef>
#include <memory>
#include <vector>

#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/VulkanLoader.h"
#include "VideoCommon/AbstractShader.h"

namespace Vulkan
{
class VKShader final : public AbstractShader
{
public:
VKShader(ShaderStage stage, std::vector<u32> spv, VkShaderModule mod);
VKShader(std::vector<u32> spv, VkPipeline compute_pipeline);
~VKShader() override;

VkShaderModule GetShaderModule() const { return m_module; }
VkPipeline GetComputePipeline() const { return m_compute_pipeline; }
bool HasBinary() const override;
BinaryData GetBinary() const override;

static std::unique_ptr<VKShader> CreateFromSource(ShaderStage stage, const char* source,
size_t length);
static std::unique_ptr<VKShader> CreateFromBinary(ShaderStage stage, const void* data,
size_t length);

private:
std::vector<u32> m_spv;
VkShaderModule m_module;
VkPipeline m_compute_pipeline;
};

} // namespace Vulkan
@@ -57,6 +57,8 @@
<ClCompile Include="Texture2D.cpp" />
<ClCompile Include="TextureCache.cpp" />
<ClCompile Include="VertexManager.cpp" />
<ClCompile Include="VKPipeline.cpp" />
<ClCompile Include="VKShader.cpp" />
<ClCompile Include="VKTexture.cpp" />
<ClCompile Include="VulkanContext.cpp" />
<ClCompile Include="VulkanLoader.cpp" />
@@ -84,6 +86,8 @@
<ClInclude Include="TextureCache.h" />
<ClInclude Include="VertexManager.h" />
<ClInclude Include="VideoBackend.h" />
<ClInclude Include="VKPipeline.h" />
<ClInclude Include="VKShader.h" />
<ClInclude Include="VKTexture.h" />
<ClInclude Include="VulkanContext.h" />
<ClInclude Include="VulkanLoader.h" />
@@ -0,0 +1,95 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <cstddef>
#include <string>
#include <utility>
#include <vector>

#include "Common/CommonTypes.h"
#include "VideoCommon/RenderState.h"
#include "VideoCommon/TextureConfig.h"

class AbstractShader;
class NativeVertexFormat;

// We use three pipeline usages:
// - GX
// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
// - 8 combined image samplers (accessible from PS)
// - 1 SSBO, accessible from PS if bounding box is enabled
// - Utility
// - Single UBO, accessible from all stages [set=0, binding=1]
// - 8 combined image samplers (accessible from PS) [set=1, binding=0-7]
// - 1 texel buffer, accessible from PS [set=2, binding=0]
// - Compute
// - 1 uniform buffer [set=0, binding=1]
// - 8 combined image samplers [set=1, binding=0-7]
// - 1 texel buffer [set=2, binding=0]
// - 1 storage image [set=3, binding=0]
enum class AbstractPipelineUsage
{
GX,
Utility
};

struct AbstractPipelineConfig
{
const NativeVertexFormat* vertex_format;
const AbstractShader* vertex_shader;
const AbstractShader* geometry_shader;
const AbstractShader* pixel_shader;
RasterizationState rasterization_state;
DepthState depth_state;
BlendingState blending_state;

union FramebufferState
{
BitField<0, 8, AbstractTextureFormat> color_texture_format;
BitField<8, 8, AbstractTextureFormat> depth_texture_format;
BitField<16, 8, u32> samples;
BitField<24, 1, u32> per_sample_shading;

bool operator==(const FramebufferState& rhs) const { return hex == rhs.hex; }
bool operator!=(const FramebufferState& rhs) const { return hex != rhs.hex; }
FramebufferState& operator=(const FramebufferState& rhs)
{
hex = rhs.hex;
return *this;
}

u32 hex;
} framebuffer_state;

AbstractPipelineUsage usage;

bool operator==(const AbstractPipelineConfig& rhs) const
{
return std::tie(vertex_format, vertex_shader, geometry_shader, pixel_shader,
rasterization_state.hex, depth_state.hex, blending_state.hex,
framebuffer_state.hex, usage) ==
std::tie(rhs.vertex_format, rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader,
rhs.rasterization_state.hex, rhs.depth_state.hex, rhs.blending_state.hex,
rhs.framebuffer_state.hex, rhs.usage);
}
bool operator!=(const AbstractPipelineConfig& rhs) const { return !operator==(rhs); }
bool operator<(const AbstractPipelineConfig& rhs) const
{
return std::tie(vertex_format, vertex_shader, geometry_shader, pixel_shader,
rasterization_state.hex, depth_state.hex, blending_state.hex,
framebuffer_state.hex, usage) <
std::tie(rhs.vertex_format, rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader,
rhs.rasterization_state.hex, rhs.depth_state.hex, rhs.blending_state.hex,
rhs.framebuffer_state.hex, rhs.usage);
}
};

class AbstractPipeline
{
public:
AbstractPipeline() = default;
virtual ~AbstractPipeline() = default;
};
@@ -0,0 +1,34 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <cstddef>
#include <string>
#include <vector>

#include "Common/CommonTypes.h"

enum class ShaderStage
{
Vertex,
Geometry,
Pixel,
Compute
};

class AbstractShader
{
public:
explicit AbstractShader(ShaderStage stage) : m_stage(stage) {}
virtual ~AbstractShader() = default;

ShaderStage GetStage() const { return m_stage; }
using BinaryData = std::vector<u8>;
virtual bool HasBinary() const = 0;
virtual BinaryData GetBinary() const = 0;

protected:
ShaderStage m_stage;
};
@@ -33,10 +33,15 @@
#include "VideoCommon/VideoCommon.h"

class AbstractRawTexture;
class AbstractPipeline;
class AbstractShader;
class AbstractTexture;
class AbstractStagingTexture;
class PostProcessingShaderImplementation;
struct TextureConfig;
struct ComputePipelineConfig;
struct AbstractPipelineConfig;
enum class ShaderStage;
enum class EFBAccessType;
enum class StagingTextureType;

@@ -69,6 +74,7 @@ class Renderer
PP_EFB_COPY_CLOCKS
};

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) {}
@@ -91,6 +97,14 @@ class Renderer
virtual std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0;

// Shader modules/objects.
virtual std::unique_ptr<AbstractShader>
CreateShaderFromSource(ShaderStage stage, const char* source, size_t length) = 0;
virtual std::unique_ptr<AbstractShader>
CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length) = 0;
virtual std::unique_ptr<AbstractPipeline>
CreatePipeline(const AbstractPipelineConfig& config) = 0;

// Ideal internal resolution - multiple of the native EFB resolution
int GetTargetWidth() const { return m_target_width; }
int GetTargetHeight() const { return m_target_height; }
@@ -160,6 +174,16 @@ class Renderer

virtual void Shutdown();

// Drawing utility shaders.
virtual void DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
u32 vertex_stride, u32 num_vertices)
{
}
virtual void DispatchComputeShader(const AbstractShader* shader, const void* uniforms,
u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z)
{
}

protected:
std::tuple<int, int> CalculateTargetScale(int x, int y) const;
bool CalculateTargetSize();
@@ -99,6 +99,8 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="AbstractStagingTexture.h" />
<ClInclude Include="AbstractPipeline.h" />
<ClInclude Include="AbstractShader.h" />
<ClInclude Include="AbstractTexture.h" />
<ClInclude Include="AsyncRequests.h" />
<ClInclude Include="AsyncShaderCompiler.h" />
@@ -374,8 +374,14 @@
<ClInclude Include="AbstractStagingTexture.h">
<Filter>Base</Filter>
</ClInclude>
<ClInclude Include="AbstractShader.h">
<Filter>Base</Filter>
</ClInclude>
<ClInclude Include="AbstractPipeline.h">
<Filter>Base</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>
</Project>